home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-04-18 | 65.7 KB | 2,972 lines |
- ********************************************************************************
- * hackdisk.s -- my very own version of trackdisk.device
- * Copyright (C) 1992 by Dan Babcock
- *
- * Version history:
- * V1.00 04/25/92 First version. Supports 880K drives only.
- * V1.01 04/28/92 Optimized CopyMem
- * V1.02 04/30/92 More intelligent write algorithm fixes performance problem
- * V1.03 05/03/92 Sets IO_ACTUAL
- * V1.04 06/24/92 Fixes a couple bugs: RemChangeInt works now, and disk change
- * errors are only reported when it makes sense.
- * CrossDOSV5/CrossPC flushed out these bugs.
- * V1.10 07/04/92 Compatible with Kickstart 1.2 and higher
- * Fixed Seek (IO_OFFSET is in bytes, not tracks)
- ********************************************************************************
-
- ;Standard register usage:
- ;A6 - ExecBase or $dff000
- ;A5 (A_DEVICE) - device ptr
- ;A4 (A_UNIT) - unit ptr
- ;A3 (A_IO) - IORequest
-
- exeobj
- multipass
-
- ;There are supposed to be two modes of operation, the RamKick (ROM) mode
- ;and the DEVS:/mountlist (DISK) mode. However, the disk mode is only
- ;half-implemented, because I decided at some point it was useless.
- ROM equ 1
- DISK equ 2 ;NOTE: Not currently functioning!
- TYPE equ ROM
-
- ;Set INFO_LEVEL to 1 for full debugging output over the internal serial port.
- INFO_LEVEL equ 0
-
- objfile 'devs:hackdisk.device'
-
- A_DEVICE equr a5
- A_UNIT equr a4
- A_IO equr a3
-
-
- VERSION equ 127
- REVISION equ 10
- MYPRI equ 0
-
- MD_NUMUNITS equ 4
- STACKSIZE equ 4000
- TASKPRI equ 5
-
- RETRYCNT equ 3
-
- ;Put a message to the serial port. Used like so:
- ;
- ;PUTDEBUG <'Init: called'>
- ;
- ;Parameters can be printed out by pushing them on the stack and
- ;adding the appropriate C printf-style % formatting commands.
-
- PUTDEBUG macro ;<msg>
- ifne INFO_LEVEL
- movem.l d0-d1/a0-a1,-(sp)
- lea .msg\@(pc),a0 ;Point to static format string
- lea 16(sp),a1 ;Point to args
- bsr KPutFmt
- movem.l (sp)+,d0-d1/a0-a1
- bra .end\@
- .msg\@:
- dc.b \1,$a,0
- even
- .end\@:
- endc
- endm
-
- *--------------------------------------------------------------------
- *
- * Driver error defines
- *
- *--------------------------------------------------------------------
-
- ;TDERR_NotSpecified EQU 20 ; general catchall
- ;TDERR_NoSecHdr EQU 21 ; couldn't even find a sector
- ;TDERR_BadSecPreamble EQU 22 ; sector looked wrong
- ;TDERR_BadSecID EQU 23 ; ditto
- ;TDERR_BadHdrSum EQU 24 ; header had incorrect checksum
- ;TDERR_BadSecSum EQU 25 ; data had incorrect checksum
- ;TDERR_TooFewSecs EQU 26 ; couldn't find enough sectors
- ;TDERR_BadSecHdr EQU 27 ; another "sector looked wrong"
- ;TDERR_WriteProt EQU 28 ; can't write to a protected disk
- ;TDERR_DiskChanged EQU 29 ; no disk in the drive
- ;TDERR_SeekError EQU 30 ; couldn't find track 0
- ;TDERR_NoMem EQU 31 ; ran out of memory
- ;TDERR_BadUnitNum EQU 32 ; asked for a unit > NUMUNITS
- ;TDERR_BadDriveType EQU 33 ; not a drive that trackdisk groks
- ;TDERR_DriveInUse EQU 34 ; someone else allocated the drive
- ;TDERR_PostReset EQU 35 ; user hit reset; awaiting doom
-
- ; STRUCTURE TIMEVAL,0
- ; ULONG TV_SECS
- ; ULONG TV_MICRO
- ; LABEL TV_SIZE
-
- ;Unit structure
-
- *--------------------------------------------------------------------
- *
- * Public portion of unit structure
- *
- *--------------------------------------------------------------------
-
- ;*------ UNIT_FLAG definitions:
-
- ;These are bogus, but I won't re-define them.
- ; BITDEF UNIT,ACTIVE,0 ; driver is active
- ; BITDEF UNIT,INTASK,1 ; running in driver's task
- BITDEF UNIT,DiskInDrive,2
- BITDEF UNIT,WriteProtected,3
-
- ; STRUCTURE TDU_PUBLICUNIT,UNIT_SIZE
- ; UWORD TDU_COMP01TRACK ; track for first precomp
- ; UWORD TDU_COMP10TRACK ; track for second precomp
- ; UWORD TDU_COMP11TRACK ; track for third precomp
- ; ULONG TDU_STEPDELAY ; time to wait after stepping
- ; ULONG TDU_SETTLEDELAY ; time to wait after seeking
- ; UBYTE TDU_RETRYCNT ; # of times to retry
- ; UBYTE TDU_PUBFLAGS ; public flags, see below
- ; UWORD TDU_CURRTRK ; track heads are over
- ; (ONLY ACCESS WHILE UNIT IS STOPPED!)
- ; ULONG TDU_CALIBRATEDELAY ; time to wait after stepping
- ; for recalibrate
- ; ULONG TDU_COUNTER ; counter for disk changes
- ; (ONLY ACCESS WHILE UNIT IS STOPPED!)
- ; LABEL TDU_PUBLICUNITSIZE
- ;*--------------------------------------------------------------------
-
- STRUCTURE MyUnit,TDU_PUBLICUNITSIZE
- STRUCT Drive_LastCheck,TV_SIZE ;last time diskchange was checked
- STRUCT ChangeIntList,MLH_SIZE ;must be initialized!!!
- LONG TDRemoveInt
-
- IFEQ TYPE-DISK
- ;not used by ROM driver
- STRUCT ChangeInt,IS_SIZE
- STRUCT TDPort,MP_SIZE
- STRUCT TDIORequest,IOTD_SIZE
- ENDC
-
- BYTE UnitNum
- BYTE MyUnit_Pad
- ;MUST be word-aligned!!!
- LABEL MyUnit_Sizeof
-
- ;*
- ;* Flags for TDU_PUBFLAGS:
- ;*
- ; BITDEF TDP,NOCLICK,0 ; set to enable noclickstart
- BITDEF TDP,VERIFY,1
-
- ;Device global structure -- accessed as positive offsets from device base
- ;Note: The stuff in LIB_SIZE is *required*. Anything else is up to you...
- STRUCTURE DeviceGlobals,LIB_SIZE
- STRUCT TaskPort,MP_SIZE ;MsgPort for task
- STRUCT Task,TC_SIZE
- STRUCT TaskStack,STACKSIZE
- STRUCT TempTimeVal,TV_SIZE
- LONG SegList ;not used for ROM version
- STRUCT TimerIORequest,IOTV_SIZE
- STRUCT TimerPort,MP_SIZE
- STRUCT LastCheck,TV_SIZE ;last time diskchange was checked
- STRUCT DiskResourceUnit,DRU_SIZE ;must be initialized!!!
- STRUCT DiskResourcePort,MP_SIZE ;must be initialized!!!
-
- ;Allocated memory pointers
- LONG RawBuffer
- LONG DecodedBuffer
- LONG VerifyBuffer
-
- STRUCT SectorLabels,11*16
- STRUCT Unit0,MyUnit_Sizeof
- STRUCT Unit1,MyUnit_Sizeof
- STRUCT Unit2,MyUnit_Sizeof
- STRUCT Unit3,MyUnit_Sizeof
- LONG GraphBase
- LONG IntBase
- LONG DiskResourceBase
- LONG CIABase
- WORD SyncCount
- WORD IndexDskLen
- LONG WriteMap
-
- LABEL StartSigs
- LONG SyncSig
- LONG BlockSig
- LABEL EndSigs
-
- BYTE BufferTrack
- BYTE BufferDrive
- BYTE DEV_FLAGS
- BYTE MotorState
- BYTE InquireBits
- LABEL MyDev_Sizeof
-
- NumSigs equ (EndSigs-StartSigs)/4
-
- ;Bit definitions for DEV_FLAGS:
- BITDEF DEV,Dirty,0 ;buffered track has been changed
- BITDEF DEV,Stopped,1 ;device is stopped
- BITDEF DEV,Verify,2 ;used by BlockInt for verify
-
- ; BITDEF DEV,Test,3 ;for debugging only
-
-
- IFEQ TYPE-DISK
- ;The first executable location. This should return an error in case someone
- ;tried to run us as a program (instead of loading us as a device).
- moveq #-1,d0
- rts
- ENDC
-
- ;A romtag structure. After your driver is brought in from disk, the
- ;disk image will be scanned for this structure to discover magic constants
- ;about you (such as where to start running you from...).
-
- RomTag:
- dc.w RTC_MATCHWORD ;$4AFC ('illegal' opcode)
- dc.l RomTag
- dc.l EndCode ;pointer to end of code
- dc.b RTF_AUTOINIT+RTF_COLDSTART ;set things up automatically
- dc.b VERSION
- dc.b NT_DEVICE ;module type (either device or library)
- dc.b MYPRI ;usually not important
- dc.l Name ;name used in OpenDevice
- dc.l IDString ;optional
- dc.l Init ;more init info
-
- IFEQ TYPE-ROM
- Name: dc.b 'trackdisk.device',0
- even
- ENDC
- IFEQ TYPE-DISK
- Name: dc.b 'hackdisk.device',0 ;this MUST be the same as the
- even ;disk-resident name!
- ENDC
-
- IDString: dc.b 'Hackdisk V1.10 - Copyright (C) 1992 by Dan Babcock',$a,0
- even
- DiskResourceName:
- dc.b 'disk.resource',0
- TimerName:
- dc.b 'timer.device',0
- GraphName:
- dc.b 'graphics.library',0
- CIAName: dc.b 'ciab.resource',0
- IntName: dc.b 'intuition.library',0
- IFEQ TYPE-DISK
- TDName: dc.b 'trackdisk.device',0
- ENDC
- even
-
- ;The romtag specified that we were "RTF_AUTOINIT". This means that the
- ;RT_INIT structure member points to one of these tables below. If the
- ;AUTOINIT bit was not set then RT_INIT would point to a routine to run.
-
- Init: dc.l MyDev_Sizeof ;data space size (at least
- ;LIB_SIZE!!)
- dc.l FuncTable ;pointer to function initializers
- dc.l DataTable ;pointer to data initializers
- dc.l InitRoutine ;routine to run
-
- FuncTable:
- dc.w -1 ;this indicates that the following are offsets,
- ;rather than absolute addresses
- dc.w Open-FuncTable ;standard system routines
- dc.w _Close-FuncTable
- dc.w Expunge-FuncTable
- dc.w .Null-FuncTable ;Reserved for future use!
- dc.w BeginIO-FuncTable ;device definitions
- dc.w AbortIO-FuncTable
- dc.w -1 ;function table end marker
- .Null: moveq #0,d0
- rts
-
- ;The data table initializes static data structures. The format is
- ;specified in exec/InitStruct routine's manual pages. The
- ;INITBYTE/INITWORD/INITLONG macros are in the file "exec/initializers.i".
- ;The first argument is the offset from the device base for this
- ;byte/word/long. The second argument is the value to put in that cell.
- ;The table is null terminated
-
- DataTable:
- INITBYTE LN_TYPE,NT_DEVICE
- INITLONG LN_NAME,Name
- INITBYTE LIB_FLAGS,LIBF_SUMUSED+LIBF_CHANGED
- INITWORD LIB_VERSION,VERSION
- INITWORD LIB_REVISION,REVISION
- INITLONG LIB_IDSTRING,IDString
- dc.w 0 ;terminate list (only one byte needed)
-
- CmdTable: dc.w Invalid-CmdTable ;0 CMD_INVALID
- dc.w Invalid-CmdTable ;1 CMD_RESET
- dc.w Read-CmdTable ;2 CMD_READ
- dc.w Write-CmdTable ;3 CMD_WRITE / ETD_
- dc.w Update-CmdTable ;4 CMD_UPDATE / ETD_
- dc.w Clear-CmdTable ;5 CMD_CLEAR / ETD_
- dc.w MyStop-CmdTable ;6 CMD_STOP / ETD_
- dc.w Start-CmdTable ;7 CMD_START
- dc.w Invalid-CmdTable ;8 CMD_FLUSH
- dc.w Motor-CmdTable ;9 TD_MOTOR / ETD_
- dc.w TDSeek-CmdTable ;10 TD_SEEK / ETD_
- dc.w Format-CmdTable ;11 TD_FORMAT
- dc.w TDRemove-CmdTable ;12 TD_REMOVE
- dc.w ChangeNum-CmdTable ;13 TD_CHANGENUM
- dc.w ChangeState-CmdTable ;14 TD_CHANGESTATE
- dc.w ProtStatus-CmdTable ;15 TD_PROTSTATUS
- dc.w TDRawRead-CmdTable ;16 TD_RAWREAD
- dc.w RawWrite-CmdTable ;17 TD_RAWWRITE
- dc.w GetDriveType-CmdTable ;18 TD_GETDRIVETYPE
- dc.w GetNumTracks-CmdTable ;19 TD_GETNUMTRACKS
- dc.w AddChangeInt-CmdTable ;20 TD_ADDCHANGEINT
- dc.w RemChangeInt-CmdTable ;21 TD_REMCHANGEINT
- dc.w GetGeometry-CmdTable ;22 TD_GETGEOMETRY
- dc.w Invalid-CmdTable ;23 TD_EJECT
- EndCmdTable:
- HighestCommand equ ((EndCmdTable-CmdTable)/2)-1
- Invalid: move.b #IOERR_NOCMD,IO_ERROR(A_IO)
- rts
-
- InitRoutine:
- ;A0 - segment
- ;D0 - device ptr
-
- ;Returns with the device ptr still in D0 if successful, otherwise zero.
-
- PUTDEBUG <'InitRoutine: Entered'>
- movem.l d1-d7/a0-a6,-(sp)
- move.l d0,A_DEVICE
- move.l a0,SegList(A_DEVICE)
- move.l 4,a6
-
- ;Initialize ports
- lea TaskPort(A_DEVICE),a0
- bsr InitPort
- lea TimerPort(A_DEVICE),a0
- bsr InitPort
-
- ;Invalidate track buffer
- bsr Clear
-
- ;Open libraries/resources
-
- lea DiskResourceName(pc),a1 ;'disk.resource'
- SYS OpenResource
- move.l d0,DiskResourceBase(A_DEVICE)
- beq .Failed
- lea CIAName(pc),a1 ;'ciab.resource'
- SYS OpenResource
- move.l d0,CIABase(A_DEVICE)
- beq .Failed
-
- lea GraphName(pc),a1
- SYS OldOpenLibrary
- move.l d0,GraphBase(A_DEVICE)
- beq .Failed
- lea IntName(pc),a1
- SYS OldOpenLibrary
- move.l d0,IntBase(A_DEVICE)
- beq .Failed
-
- ;Allocate CHIP memory. (Note: This will change when I support other drive
- ;types).
- move.l #DISK_RawBufSize,d0
- move.l #MEMF_CHIP,d1
- SYS AllocMem
- move.l d0,RawBuffer(A_DEVICE)
- beq .Failed
-
- move.l #512*11,d0
- move.l #MEMF_CHIP,d1
- SYS AllocMem
- move.l d0,DecodedBuffer(A_DEVICE)
- beq .Failed
-
- move.l #(1088*11)+2,d0
- move.l #MEMF_CHIP,d1
- SYS AllocMem
- move.l d0,VerifyBuffer(A_DEVICE)
- beq .Failed
-
- ;Set up disk.resource structure.
- lea DiskResourcePort(A_DEVICE),a0
- bsr InitPort
- move.l a0,DiskResourceUnit+MN_REPLYPORT(A_DEVICE)
- ; move.b #PA_SIGNAL,MP_FLAGS(a0) ;not needed (0)
- lea Name(pc),a0
- move.l a0,DiskResourceUnit+LN_NAME(A_DEVICE)
-
- move.l A_DEVICE,DiskResourceUnit+DRU_DISCBLOCK+IS_DATA(A_DEVICE)
- move.l A_DEVICE,DiskResourceUnit+DRU_DISCSYNC+IS_DATA(A_DEVICE)
- move.l A_DEVICE,DiskResourceUnit+DRU_INDEX+IS_DATA(A_DEVICE)
- lea BlockInt(pc),a0
- move.l a0,DiskResourceUnit+DRU_DISCBLOCK+IS_CODE(A_DEVICE)
- lea SyncInt(pc),a0
- move.l a0,DiskResourceUnit+DRU_DISCSYNC+IS_CODE(A_DEVICE)
- lea IndexInt(pc),a0
- move.l a0,DiskResourceUnit+DRU_INDEX+IS_CODE(A_DEVICE)
- moveq #NT_INTERRUPT,d0
- move.b d0,DiskResourceUnit+DRU_DISCBLOCK+LN_TYPE(A_DEVICE)
- move.b d0,DiskResourceUnit+DRU_DISCSYNC+LN_TYPE(A_DEVICE)
- move.b d0,DiskResourceUnit+DRU_INDEX+LN_TYPE(A_DEVICE)
-
- ;Initialize timer IORequest (except signal)
- lea TimerPort(A_DEVICE),a0
- ; move.b #PA_SIGNAL,MP_FLAGS(a0) ;not needed (0)
- move.l a0,TimerIORequest+MN_REPLYPORT(A_DEVICE)
- lea TimerName(pc),a0
- moveq #UNIT_MICROHZ,d0
- lea TimerIORequest(A_DEVICE),a1
- moveq #0,d1
- SYS OpenDevice
- tst.l d0
- bne .Failed
-
- ;*** Set up unit structures
- moveq #0,d2
- lea Unit0(A_DEVICE),A_UNIT
- .UnitLoop:
-
- IFEQ TYPE-DISK
- lea TDPort(A_UNIT),a0
- bsr InitPort
- move.l a0,TDIORequest+MN_REPLYPORT(A_UNIT)
- ENDC
-
- move.b d2,UnitNum(A_UNIT)
- lea ChangeIntList(A_UNIT),a0
- NEWLIST a0
-
- bset #1,TDU_PUBFLAGS(A_UNIT) ;verify ON!
- moveq #-1,d0
- move.w #80,TDU_COMP01TRACK(A_UNIT)
- move.w d0,TDU_COMP10TRACK(A_UNIT)
- move.w d0,TDU_COMP11TRACK(A_UNIT)
- move.l #3*1000,TDU_STEPDELAY(A_UNIT)
- move.l #15*1000,TDU_SETTLEDELAY(A_UNIT)
- move.b #RETRYCNT,TDU_RETRYCNT(A_UNIT)
- move.l #4*1000,TDU_CALIBRATEDELAY(A_UNIT)
-
- lea MyUnit_Sizeof(A_UNIT),A_UNIT
- addq.b #1,d2
- cmp.b #MD_NUMUNITS,d2
- blo .UnitLoop
-
- ;Set up task
- move.b #PA_IGNORE,TaskPort+MP_FLAGS(A_DEVICE)
- move.b #NT_MSGPORT,TaskPort+LN_TYPE(A_DEVICE)
- lea Task(A_DEVICE),a1 ;Task Control Block
- lea Name(pc),a0
- move.l a0,LN_NAME(a1)
- move.b #TASKPRI,LN_PRI(a1)
- move.b #NT_TASK,LN_TYPE(a1)
- lea TaskCode(pc),a2 ;initial PC
- sub.l a3,a3 ;final PC
- lea TaskStack(A_DEVICE),a0
- move.l a0,TC_SPLOWER(a1)
- lea STACKSIZE(a0),a0
- move.l a0,TC_SPUPPER(a1)
-
- move.l A_DEVICE,-(a0) ;pass device pointer to task
- move.l a0,TC_SPREG(a1)
- move.l a1,TaskPort+MP_SIGTASK(A_DEVICE)
- SYS AddTask
-
- move.l A_DEVICE,d0
- PUTDEBUG <'InitRoutine: Done'>
- .End: movem.l (sp)+,d1-d7/a0-a6
- rts
- .Failed:
- PUTDEBUG <'InitRoutine: Failed'>
- bsr CloseEverything
- moveq #0,d0
- bra .End
-
- InitPort:
- ;Enter with pointer to port in A0. All registers preserved.
- push a0
- lea MP_MSGLIST(a0),a0
- NEWLIST a0
- pop a0
- rts
-
- CloseEverything:
- ;This routines cleans up device stuff. Enter with A_DEVICE.
-
- movem.l d0-d1/a0-a1/a6,-(sp)
- move.l 4,a6
- ;Close timer
- tst.b TimerIORequest+IO_ERROR(A_DEVICE)
- beq .SkipTimer
- lea TimerIORequest(A_DEVICE),a1
- SYS CloseDevice
- .SkipTimer:
-
- ;Remove task
- lea Name(pc),a1
- SYS FindTask
- tst.l d0
- beq .SkipTask
- move.l d0,a1
- SYS RemTask
- .SkipTask:
-
- ;Close libraries
- move.l GraphBase(A_DEVICE),d0
- beq .SkipGfx
- move.l d0,a1
- SYS CloseLibrary
- .SkipGfx:
- move.l IntBase(A_DEVICE),d0
- beq .SkipInt
- move.l d0,a1
- SYS CloseLibrary
- .SkipInt:
-
- ;Free CHIP RAM. (Note: This will change when I support other drive types).
- move.l RawBuffer(A_DEVICE),d0
- beq .SkipRaw
- move.l d0,a1
- move.l #DISK_RawBufSize,d0
- SYS FreeMem
- .SkipRaw:
- move.l DecodedBuffer(A_DEVICE),d0
- beq .SkipDecoded
- move.l d0,a1
- move.l #512*11,d0
- SYS FreeMem
- .SkipDecoded:
- move.l VerifyBuffer(A_DEVICE),d0
- beq .SkipVerify
- move.l d0,a1
- move.l #(1088*11)+2,d0
- SYS FreeMem
- .SkipVerify:
- .End: movem.l (sp)+,d0-d1/a0-a1/a6
- rts
-
- ;******************************* Task code ******************************
-
- TaskCode:
- PUTDEBUG <'Task: Entered'>
- move.l 4,a6
-
- ;Grab the arguments passed down from our parent
- move.l 4(sp),A_DEVICE ;Device pointer
-
- ;General initialization
-
- lea StartSigs(A_DEVICE),a2
- moveq #NumSigs-1,d2 ;Number of signals to allocate-1
- .SigLoop:
- moveq #-1,d0 ;-1 is any signal at all
- SYS AllocSignal ;Allocate signals for I/O interrupts
- moveq #0,d1 ;Convert bit number signal mask
- bset d0,d1
- move.l d1,(a2)+ ;Save in unit structure
- dbra d2,.SigLoop
-
- moveq #-1,d0 ;-1 is any signal at all
- SYS AllocSignal ;Allocate a signal
- move.b d0,TaskPort+MP_SIGBIT(A_DEVICE)
- move.b #PA_SIGNAL,TaskPort+MP_FLAGS(A_DEVICE) ;Make message port "live"
-
- moveq #-1,d0 ;-1 is any signal at all
- SYS AllocSignal ;Allocate a signal
- move.b d0,TimerPort+MP_SIGBIT(A_DEVICE)
- move.l ThisTask(a6),TimerPort+MP_SIGTASK(A_DEVICE)
-
- moveq #-1,d0 ;-1 is any signal at all
- SYS AllocSignal ;Allocate a signal
- move.b d0,DiskResourcePort+MP_SIGBIT(A_DEVICE)
- move.l ThisTask(a6),DiskResourcePort+MP_SIGTASK(A_DEVICE)
-
- bsr GetDrive
- bsr Inquire
-
- ;Disk version only:
- ;Open trackdisk.device for all valid units and install diskchange handler.
- IFEQ TYPE-DISK
- moveq #MD_NUMUNITS-1,d2
- move.b InquireBits(A_DEVICE),d3
- lea Unit0(A_DEVICE),A_UNIT
- .TDLoop:
- moveq #-1,d0 ;-1 is any signal at all
- SYS AllocSignal ;Allocate a signal
- move.b d0,TDPort+MP_SIGBIT(A_UNIT)
- move.l ThisTask(a6),TDPort+MP_SIGTASK(A_UNIT)
- moveq #0,d0
- move.b UnitNum(A_UNIT),d0
- btst d0,d3
- beq .NextTD
- moveq #0,d1
- lea TDName(pc),a0
- lea TDIORequest(A_DEVICE),a1
- SYS OpenDevice
- tst.l d0
- beq .TDOK
- ;Should never get here
- .. move.w #$0f00,$dff180
- bra ..
- .TDOK:
-
- ;Install a diskchange handler via TD_ADDCHANGEINT.
- lea TDIORequest(A_DEVICE),a1
- lea ChangeInt(A_UNIT),a0
- lea ChangeIntCode(pc),a2
- move.l a2,IS_CODE(a0)
- move.l A_UNIT,IS_DATA(a0)
- move.l a0,IO_DATA(a1)
- move.l #IS_SIZE,IO_LENGTH(a1) ;this is dumb
- move.w #TD_ADDCHANGEINT,IO_COMMAND(a1)
- SYS SendIO
-
- .NextTD: lea MyUnit_Sizeof(A_UNIT),A_UNIT
- dbra d2,.TDLoop
- ENDC
-
- IFNE INFO_LEVEL
- push d0
- moveq #0,d0
- move.b InquireBits(A_DEVICE),d0
- move.l d0,-(sp)
- PUTDEBUG <'InquireBits=%lx'>
- addq.l #4,sp
- pop d0
- ENDC
-
- bsr SeekZeroAll ;Send all drives to track zero.
- bsr FreeDrive
-
- bra .NextMessage
-
- ;Main loop: Wait for a new message and handle disk changes.
-
- .MainLoop:
-
- ; PUTDEBUG <'Task: Waiting'>
- moveq #0,d0
- move.b MP_SIGBIT+TaskPort(A_DEVICE),d1
- bset d1,d0
- move.l #500000,d1
- bsr TimeOutWait
- tst.l d0
- bne .NextMessage
-
- ;0.5 seconds have passed without receiving any work, so we check for disk
- ;changes, then drop into .NextMessage.
- ;Note: A6 undefined.
-
- .CheckDiskChange:
- moveq #MD_NUMUNITS-1,d2
- move.b InquireBits(A_DEVICE),d3
- lea Unit0(A_DEVICE),A_UNIT
- bsr GetDrive
- move.l TimerIORequest+IO_DEVICE(A_DEVICE),a6
-
- ;Algorithm (for each unit):
- ;Check hardware diskchange bit. If disk was ejected, seek to track zero,
- ;clear DiskInDrive bit. End.
- ;If no disk in drive, check NoClick bit. If set, immediately step toward
- ;track zero (using custom step routine). If clear, check the
- ;Drive_LastChecked time against the current time. If less than 2.5 seconds,
- ;End. If >2.5 seconds, step the head toward track zero, unless already on
- ;track zero. Check the hardware diskchange bit. If a disk is present in the
- ;drive set the DiskInDrive bit and check the hardware write protect status,
- ;setting WriteProtected as needed. End.
- ;Note: We also increment the diskchange counter in the public part of the
- ;unit structure, if a disk was inserted or removed.
-
- .UnitLoop:
- move.b UnitNum(A_UNIT),d4
- btst d4,d3
- beq .NoCheck
-
- lea TempTimeVal(A_DEVICE),a0
- bsr GetSysTime
-
- btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
- beq .ThinkNoDisk
- bsr SelectDriveSameMotor
- btst #CIAB_DSKCHANGE,ciaapra
- bne .NoChange
- ;Disk was removed.
- bclr #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
- bsr SeekZero
- bsr MotorOff
- bsr Clear
- bra .Change
- .ThinkNoDisk:
- btst #TDPB_NOCLICK,TDU_PUBFLAGS(A_UNIT)
- beq .Click
- bsr SelectDriveSameMotor
- bset #CIAB_DSKDIREC,ciabprb ;set to "out" (lower tracks)
- bset #CIAB_DSKSTEP,ciabprb
- bclr #CIAB_DSKSTEP,ciabprb ;step head
- bset #CIAB_DSKSTEP,ciabprb
- bsr Deselect
- move.l TDU_SETTLEDELAY(A_UNIT),d0
- bsr delay
- bsr SelectDriveSameMotor
- bra .ChkChg
- .Click:
- lea Drive_LastCheck(A_UNIT),a1
- SYS SubTime
- cmp.l #2,TV_SECS(a0)
- blo .NoCheck
- bhi .SkipMicro
- cmp.l #500000,TV_MICRO(a0)
- blo .NoCheck
- .SkipMicro:
- lea Drive_LastCheck(A_UNIT),a0
- bsr GetSysTime
- move.b UnitNum(A_UNIT),d2 ;for Seek
- move.w TDU_CURRTRK(A_UNIT),d3
- subq.w #2,d3
- bpl .NotZero
- moveq #2,d3
- .NotZero: bsr SelectDriveSameMotor
- bsr Seek
- .ChkChg: btst #CIAB_DSKCHANGE,ciaapra
- beq .NoChange ;no disk in drive
- bset #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
- bclr #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
- btst #CIAB_DSKPROT,ciaapra
- bne .Change ;not write protected
- bset #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
- .Change:
- bsr Deselect
- addq.l #1,TDU_COUNTER(A_UNIT)
-
- ;This code notifies users of TD_REMOVE and TD_ADDCHANGEINT via Cause.
-
- push a6
- move.l 4,a6
- SYS Forbid
- move.l TDRemoveInt(A_UNIT),d0
- beq .NoRemoveInt
- move.l d0,a1
- SYS Cause
- .NoRemoveInt:
- move.l ChangeIntList+LH_HEAD(A_UNIT),a2
- .IntLoop: move.l (a2),d0
- beq .DoneInt
- move.l IO_DATA(a2),a1
- move.l d0,a2
- SYS Cause
- bra .IntLoop
- .DoneInt: SYS Permit
- pop a6
- .NoChange:
- lea LastCheck(A_DEVICE),a0
- bsr GetSysTime
- .NoCheck:
- lea MyUnit_Sizeof(A_UNIT),A_UNIT
- dbra d2,.UnitLoop
- move.l 4,a6
- bsr FreeDrive
-
- .NextMessage:
- btst #DEVB_Stopped,DEV_FLAGS(A_DEVICE) ;See if we are stopped
- bne .MainLoop ;Device is stopped, ignore messages
-
- lea TaskPort(A_DEVICE),a0
- SYS GetMsg ;Get the next request
- tst.l d0
- beq .MainLoop ;no message?
- move.l d0,A_IO ;Do this request
- move.l IO_UNIT(A_IO),A_UNIT
-
- ;Handle TDB_EXTCOM and dispatch command
- move.w IO_COMMAND(A_IO),d0
-
- IFNE INFO_LEVEL
- swap d0
- clr.w d0
- swap d0
- move.l d0,-(sp)
- PUTDEBUG <'Command=%ld'>
- addq.l #4,sp
- ENDC
-
- bclr #TDB_EXTCOM,d0
- beq .NoExt
- move.l TDU_COUNTER(A_UNIT),d1
- cmp.l IOTD_COUNT(A_IO),d1
- bls .NoExt
- .ChgErr: PUTDEBUG <'Change error!'>
- move.b #TDERR_DiskChanged,IO_ERROR(A_IO)
- bra .Reply
- .NoExt: move.l #%000000111000111000011100,d1 ;specifies which commands should check for disk
- btst d0,d1
- beq .NoDiskNeeded
- btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
- beq .ChgErr
- .NoDiskNeeded:
- bsr GetDrive
- lea CmdTable(pc),a0
- add.w d0,d0
- add.w (a0,d0.w),a0
- moveq #0,d7 ;clear error flag
- jsr (a0)
- bsr FreeDrive
- cmp.w #TD_ADDCHANGEINT,IO_COMMAND(A_IO)
- beq .SkipReply
- .Reply: move.l A_IO,a1
- SYS ReplyMsg
- .SkipReply:
-
- ;If at least 0.5 seconds has passed since last checking diskchange, do so
- ;now.
- move.l TimerIORequest+IO_DEVICE(A_DEVICE),a6
- lea TempTimeVal(A_DEVICE),a0
- bsr GetSysTime
- lea LastCheck(A_DEVICE),a1
- SYS SubTime
- tst.l TV_SECS(a0)
- bne .CheckDiskChange
- cmp.l #500000,TV_MICRO(a0)
- bhs .CheckDiskChange
- lea LastCheck(A_DEVICE),a0
- bsr GetSysTime
- move.l 4,a6
- bra .NextMessage
-
- GetSysTime:
- cmp.w #36,LIB_VERSION(a6)
- bls .OldKS
- jmp _LVOGetSysTime(a6)
- .OldKS:
- ;Compatibility routine for 1.2/1.3
- movem.l d0-d1/a0-a2/a6,-(sp)
- move.l a0,a2
- lea TimerIORequest(A_DEVICE),a1
- move.l 4,a6
- move.w #TR_GETSYSTIME,IO_COMMAND(a1)
- SYS DoIO
- lea TimerIORequest(A_DEVICE),a1
- move.l IOTV_TIME+TV_SECS(a1),TV_SECS(a2)
- move.l IOTV_TIME+TV_MICRO(a1),TV_MICRO(a2)
- movem.l (sp)+,d0-d1/a0-a2/a6
- rts
-
-
- IFEQ TYPE-DISK
- ChangeIntCode:
- rts
- ENDC
-
- ;******************** External device routines ***********************
-
- Open:
-
- ;A6 - device ptr
- ;A1 - IORequest
- ;D0 - unit number
- ;D1 - flags (not used)
-
- PUTDEBUG <'Open: Entered'>
- moveq #MD_NUMUNITS,d1
- cmp.l d1,d0
- bhs .Error
- btst d0,InquireBits(a6)
- beq .Error
- lea Unit0(a6),a0
- mulu.w #MyUnit_Sizeof,d0
- add.l d0,a0
- move.l a0,IO_UNIT(a1)
- addq.w #1,LIB_OPENCNT(a6)
- clr.b IO_ERROR(a1) ;no error
- move.b #NT_REPLYMSG,LN_TYPE(a1) ;Mark IORequest as "complete"
- PUTDEBUG <'Open: Done'>
- rts
- .Error:
- PUTDEBUG <'Open: Failed'>
-
- ;VirusX didn't like this.
- ; move.b #IOERR_OPENFAIL,IO_ERROR(a1)
-
- move.b #TDERR_BadUnitNum,IO_ERROR(a1)
- rts
-
- _Close:
-
- ;A6 - device ptr
- ;A1 - IORequest
-
- ;Must return either 0 or, if the device wishes to be unloaded, the segment
- ;list.
-
- PUTDEBUG <'Close: Called'>
- IFEQ TYPE-ROM
- moveq #0,d0
- rts
- ENDC
-
- IFEQ TYPE-DISK
- moveq #0,d0
- subq.w #1,LIB_OPENCNT(a6) ;Mark us as having one fewer openers
- bne .End ;See if there is anyone left with us open
- btst #LIBB_DELEXP,LIB_FLAGS(a6) ;See if we have a delayed expunge pending
- beq .End
- bsr Expunge ;Expunge will return with SegList in D0
- .End: rts
- ENDC
-
- Expunge:
-
- ;A6 - device ptr
-
- ;Must return either 0 or SegList ptr in D0.
-
- IFEQ TYPE-ROM
- moveq #0,d0
- rts
- ENDC
-
- IFEQ TYPE-DISK
- movem.l d2/a2/a6,-(sp)
- tst.w LIB_OPENCNT(a6) ;See if anyone has us open
- bne .Delay
- bsr CloseEverything ;clean up
- move.l SegList(a6),d2 ;Store our seglist in d2
- move.l a6,a1 ;Unlink from device list
- move.l a6,a2 ;save
- move.l 4,a6
- SYS Remove ;Remove first (before FreeMem)
- move.l a2,a1 ;device ptr
- moveq #0,d0
- move.w LIB_NEGSIZE(a2),d0
- sub.l d0,a1 ;Calculate base of functions
- add.w LIB_POSSIZE(a2),d0 ;Calculate size of functions + data area
- SYS FreeMem
- move.l d2,d0 ;Set up our return value
- .End: movem.l (sp)+,d2/a2/a6
- rts
- .Delay: bset #LIBB_DELEXP,LIB_FLAGS(a6) ;Set the delayed expunge flag
- moveq #0,d0
- bra .End
- ENDC
-
- BeginIO:
- ;A1 - IORequest
- ;A6 - device ptr
-
- movem.l d7/a3-a6,-(sp)
- move.l a1,A_IO
- move.l IO_UNIT(A_IO),A_UNIT
- move.l a6,A_DEVICE
- move.l 4,a6
-
- clr.b IO_ERROR(A_IO)
- move.b #NT_MESSAGE,LN_TYPE(A_IO) ;So WaitIO() is guaranteed to work
-
- ;Decide whether the command is immediate or queued
- move.w IO_COMMAND(A_IO),d1
- bclr #15,d1
- cmp.w #HighestCommand,d1
- bhi .BadCmd
- move.l #%111011001111000111000011,d0 ;specifies what commands are immediate
- btst d1,d0
- beq .Queue
- add.w d1,d1
- lea CmdTable(pc),a0
- add.w (a0,d1.w),a0
- moveq #0,d7 ;clear error flag
- jsr (a0)
- .Reply: btst #IOB_QUICK,IO_FLAGS(A_IO)
- bne .End
- move.l A_IO,a1
- SYS ReplyMsg
- .End: movem.l (sp)+,d7/a3-a6
- rts
- .Queue: bclr #IOB_QUICK,IO_FLAGS(A_IO) ;We did NOT complete this quickly
- lea TaskPort(A_DEVICE),a0
- move.l A_IO,a1
- SYS PutMsg
- bra .End
- .BadCmd: move.b #IOERR_NOCMD,IO_ERROR(a1)
- bra .Reply
-
- AbortIO:
-
- ;A6 - device ptr
- ;A1 - IORequest
-
- move.b #IOERR_ABORTED,IO_ERROR(a1) ;We always say we succeed(ed)
- moveq #0,d0 ;another success code
- rts
-
- ;**************** Device command (IO_COMMAND) routines *************************
-
- ;Note: A6 = ExecBase upon entering a command routine.
- ;The low-level routines load $DFF000 into A6 when needed.
-
- SaveRegs equrl d0-d3/d6-d7/a0-a2/a6
-
- Read:
- PUTDEBUG <'Read: Called'>
- movem.l SaveRegs,-(sp)
- bsr RWSetup
- tst.l d7
- bne FinishRW
-
- bsr DISK_Update ;required because of complex write scheme
- ;Check for sector label nonsense
- btst #7,IO_COMMAND(A_IO)
- beq .NoLabel
- move.l IOTD_SECLABEL(A_IO),d2
- bne ReadSecLabel
- .NoLabel:
-
- bsr DISK_Read
- PUTDEBUG <'Read: Done'>
- bra FinishRW
- Format:
- Write: movem.l SaveRegs,-(sp)
- bsr RWSetup
- tst.l d7
- bne FinishRW
-
- ;Check for sector label nonsense
- btst #7,IO_COMMAND(A_IO)
- beq .NoLabel
- move.l IOTD_SECLABEL(A_IO),d2
- bne WriteSecLabel
- .NoLabel:
-
- bsr DISK_Write
- FinishRW: clr.l IO_ACTUAL(A_IO)
- move.b d7,IO_ERROR(A_IO)
- bne .Skip
- move.l IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
- .Skip: movem.l (sp)+,SaveRegs
- rts
- RWSetup:
- move.l #512*11,d6
- move.l #_custom,a6
- move.l IO_LENGTH(A_IO),d0
- move.l IO_OFFSET(A_IO),d1
- move.l IO_DATA(A_IO),a0
- move.l d1,d2 ;offset
- add.l d0,d2 ;length
- cmp.l #901120,d2
- bhi .Error
- bsr SelectDrive
- bra SelectSide
- .Error: moveq #DISK_BadParameter,d7
- rts
-
- ;Here are the routines for dealing with read/write requests that involve
- ;the sector label. Note that, unlike the usual read/write routines of my
- ;trackdisk, the normal parameter restrictions (e.g. IO_OFFSET and IO_LENGTH
- ;must be a multiple of 512) MUST be observed. And I don't check for illegal
- ;parameters, either. These routines are optimized for compactness rather
- ;than performance because they are rarely (if ever) used.
- ;Enter with:
- ;IO_LENGTH: D0
- ;IO_OFFSET: D1
- ;IO_DATA: A0
-
- ReadSecLabel:
- PUTDEBUG <'READSECLABEL!'>
-
- move.l d2,a1
- move.l d0,d2
- move.l #512,d0
- .Read: bsr DISK_Read
- move.l d1,d3
- add.l d0,d1
- add.l d0,a0
- divu.w d6,d3 ;offset/tracksize
- swap d3 ;remainder=sector# (in bytes)
- lsr.w #5,d3 ;divide by 32 to get label offset
- lea SectorLabels(A_DEVICE),a2
- lea (a2,d3.w),a2 ;get pointer to label
- moveq #3,d3
- .. move.l (a2)+,(a1)+ ;copy label
- dbra d3,..
- sub.l d0,d2
- bne .Read
- bra FinishRW
-
- WriteSecLabel:
- PUTDEBUG <'WRITESECLABEL!'>
-
- move.l d2,a1
- move.l d0,d2
- .Write:
- move.l d1,d3
- divu.w d6,d3 ;offset/tracksize
- swap d3 ;remainder=sector# (in bytes)
- lsr.w #5,d3 ;divide by 32 to get label offset
-
- ;We check to see whether we can write a full track. (Somehow, I can't
- ;ignore performance completely :-)).
- tst.w d3
- bne .Read
- cmp.l d6,d0
- bhs .FullTrack
- .Read: moveq #0,d0
- bsr DISK_Read ;force a track read
- lea SectorLabels(A_DEVICE),a2
- lea (a2,d3.w),a2 ;get pointer to label
- moveq #3,d3
- .. move.l (a1)+,(a2)+ ;copy label
- dbra d3,..
- move.l #512,d0
- .L1: bsr DISK_Write
- add.l d0,d1
- add.l d0,a0
- sub.l d0,d2
- bne .Write
- bra FinishRW
- .FullTrack:
- lea SectorLabels(A_DEVICE),a2
- moveq #((16*11)/4)-1,d3
- .. move.l (a1)+,(a2)+ ;copy all sector labels
- dbra d3,..
- move.l d6,d0
- bra .L1
-
- Update: move.l d7,-(sp)
- moveq #0,d7
- bsr DISK_Update
- move.b d7,IO_ERROR(A_IO)
- move.l (sp)+,d7
- rts
-
- Clear:
- ;This routine marks the track buffer as invalid.
- st BufferDrive(A_DEVICE)
- bclr #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
- rts
-
- Motor:
- ;Controls the drive motor
- ;If IO_LENGTH=1, motor on
- ; IO_LENGTH=0, motor off
- ;Old motor state (0=off, anything else means on) stored in IO_ACTUAL.
-
- push d2
- clr.l IO_ACTUAL(A_IO)
- move.b UnitNum(A_UNIT),d2
- btst d2,MotorState(A_DEVICE)
- beq .WasOff
- move.l #1,IO_ACTUAL(A_IO)
- .WasOff: tst.l IO_LENGTH(A_IO)
- beq .Off
- ;Turn motor on
- bsr SelectDrive
- bra .End
- .Off:
- ;Turn motor off
- bclr d2,MotorState(A_DEVICE)
- bsr Deselect
- bset #CIAB_DSKMOTOR,ciabprb ;motor off
- addq.b #3,d2
- bclr d2,ciabprb ;select drive X
- .End: pop d2
- rts
-
- ChangeState:
- ;Returns whether there is a disk currently in the drive
- ;IO_ACTUAL=0 if disk in drive
- ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
-
- clr.l IO_ACTUAL(A_IO)
- btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
- bne .End
- move.l #1,IO_ACTUAL(A_IO) ;no disk in drive
- .End: rts
-
- ChangeNum:
- ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
- move.l TDU_COUNTER(A_UNIT),IO_ACTUAL(A_IO)
- rts
-
- ProtStatus:
- ;IO_ACTUAL=0 if disk is not write protected
- ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
-
- ;Very important for compatibility: we must return an error (disk changed)
- ;if there's no disk in the drive.
- btst #UNITB_DiskInDrive,UNIT_FLAGS(A_UNIT)
- bne .OK
- move.b #TDERR_DiskChanged,IO_ERROR(A_IO)
- rts
-
- .OK: clr.l IO_ACTUAL(A_IO)
- btst #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
- beq .End ;not write protected
- move.l #1,IO_ACTUAL(A_IO) ;no disk in drive
- .End: rts
-
- GetDriveType:
- ;Returns type of drive in IO_ACTUAL
- ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
- move.l #DRIVE3_5,IO_ACTUAL(A_IO)
- rts
-
- GetNumTracks:
- ;Returns number of tracks (note: not cylinders) in IO_ACTUAL
- ;* EXECUTED IMMEDIATELY, POSSIBILY WITHIN AN INTERRUPT *
- move.l #160,IO_ACTUAL(A_IO)
- rts
-
- GetGeometry:
- ;Fills in DriveGeometry structure pointed to by IO_DATA
- push a0
- move.l IO_DATA(A_IO),a0
- move.l #512,dg_SectorSize(a0) ;in bytes
- move.l #1760,dg_TotalSectors(a0) ;total # of sectors on drive
- move.l #80,dg_Cylinders(a0) ;number of cylinders
- move.l #22,dg_CylSectors(a0) ;number of sectors/cylinder
- move.l #2,dg_Heads(a0) ;number of surfaces
- move.l #11,dg_TrackSectors(a0) ;number of sectors/track
- clr.l dg_BufMemType(a0) ;preferred buffer memory type
- clr.b dg_DeviceType(a0) ;codes as defined in the SCSI-2 spec
- move.b #DGF_REMOVABLE,dg_Flags(a0) ;flags, including removable
- clr.w dg_Reserved(a0)
- pop a0
- rts
-
- MyStop: bset #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
- rts
- Start: movem.l d0-d1/a0-a1,-(sp)
- bset #DEVB_Stopped,DEV_FLAGS(A_DEVICE)
- moveq #0,d0
- move.b MP_SIGBIT+TaskPort(A_DEVICE),d1
- bset d1,d0
- lea Task(A_DEVICE),a1
- SYS Signal
- movem.l (sp)+,d0-d1/a0-a1
- rts
-
- ;Note: This seek routine can't really be depended on by user programs,
- ;because the disk.resource will corrupt the side select bit.
- TDSeek: push d3
- bsr SelectDriveSameMotor
- move.l IO_OFFSET(A_IO),d3
- divu.w #512*11,d3
- cmp.w #159,d3
- bhi .Error
- bsr Seek
- .End: pop d3
- rts
- .Error: move.b #TDERR_NotSpecified,IO_ERROR(A_IO)
- bra .End
-
- TDRawRead:
- movem.l d0-d1/d3/a6,-(sp)
- move.l #_custom,a6
- moveq #0,d1
- bsr DoRaw
- movem.l (sp)+,d0-d1/d3/a6
- rts
-
- RawWrite:
- movem.l d0-d1/d3/a6,-(sp)
- move.l #_custom,a6
- btst #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
- bne .WPErr
- move.w #1<<14,d1
- bsr DoRaw
- move.l #3*1000,d0
- bsr delay ;post-write delay
- .End: movem.l (sp)+,d0-d1/d3/a6
- rts
- .WPErr: move.b #TDERR_WriteProt,IO_ERROR(A_IO)
- bra .End
-
- DoRaw:
- ;Enter with bit 14 set in d1 according to read/write (for dsklen)
- ;Uses d0-d1/d3 (not saved)
- ;Assumes $dff000 in A6
-
- clr.l IO_ACTUAL(A_IO)
- bsr SelectDrive
- move.l IO_OFFSET(A_IO),d3
- cmp.l #159,d3
- bhi .Err
- bsr Seek
- tst.l d7
- bne .Err
-
- move.l IO_DATA(A_IO),dskpt(a6)
- move.w #ADKF_WORDSYNC,adkcon(a6)
- btst #IOTDB_WORDSYNC,IO_FLAGS(A_IO)
- beq .SkipSync
- move.w #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
- .SkipSync:
- move.w #ADKF_MSBSYNC,adkcon(a6)
- move.w #ADKF_SETCLR+ADKF_FAST+ADKF_MFMPREC,adkcon(a6)
- bsr PreComp
- move.w #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6) ;enable disk DMA
- move.w #$4489,dsksync(a6) ;set magic sync word
- bsr ClearSigs
- move.w #INTF_SETCLR+INTF_DSKBLK,intena(a6) ;enable int
- move.l IO_LENGTH(A_IO),d0
-
- cmp.l #32766,d0
- bhi .Error
- lsr.l #1,d0
- bset #15,d0
- or.w d1,d0
-
- btst #IOTDB_INDEXSYNC,IO_FLAGS(A_IO)
- beq .NoIndex
- move.w d0,IndexDskLen(A_DEVICE)
- bsr EnableIndex
- bra .Wait
- .NoIndex:
-
- move.w d0,dsklen(a6)
- move.w d0,dsklen(a6)
- .Wait: move.l BlockSig(A_DEVICE),d0
- move.l #900*1000,d1
- bsr TimeOutWait
- beq .Error
- move.w #0,dsklen(a6)
- move.w #INTF_DSKBLK,intena(a6) ;disable int
- bsr DisableIndex
- bsr ClearSigs
- .End: tst.b IO_ERROR(A_IO)
- bne .EndRts
- move.l IO_LENGTH(A_IO),IO_ACTUAL(A_IO)
- .EndRts: rts
- .Error: bsr StopDMA
- .Err: move.b #TDERR_NotSpecified,IO_ERROR(A_IO)
- bra .End
-
- AddChangeInt:
- ;This command uses the linkage fields of the IORequest to add a ChangeInt
- ;request to a list maintained in the unit structure. This must NOT be
- ;ReplyMsg'ed to avoid destroying the linkage fields - a special compare in
- ;the task code handles this.
- movem.l d0-d1/a0-a1,-(sp)
- move.l A_IO,a1
- lea ChangeIntList(A_UNIT),a0
- SYS AddHead ;could use any list add routine
- movem.l (sp)+,d0-d1/a0-a1
- rts
-
- RemChangeInt:
- ;This command unlinks the AddChangeInt request from the task's port.
- ;This is (and must be) an immediate command.
- ;Note that because this command occurs asyncronously to the task code, the
- ;task code that scans the ChangeInt list must be protected with a Forbid.
-
- movem.l d0-d1/a0-a1,-(sp)
- SYS Forbid
- move.l A_IO,a1
- SYS Remove
- SYS Permit
- movem.l (sp)+,d0-d1/a0-a1
- rts
-
- TDRemove:
- ;This command is obsolete, but is unfortuntely used by the ROM filesystem.
- ;It accepts an Interrupt structure (for Cause) in IO_DATA. Only one user per
- ;unit is permitted.
-
- tst.l TDRemoveInt(A_UNIT)
- beq .Ok
- move.b #TDERR_DriveInUse,IO_ERROR(A_IO)
- rts
- .Ok: move.l IO_DATA(A_IO),TDRemoveInt(A_UNIT)
- rts
-
- GetDrive:
- ;Obtain the use of this unit via disk.resource.
-
- ; PUTDEBUG <'GetDrive: Called'>
- movem.l d0-d1/a0-a2/a6,-(sp)
- lea DiskResourceUnit(A_DEVICE),a2
- .GetUnit:
- move.l DiskResourceBase(A_DEVICE),a6
- move.l a2,a1
- jsr DR_GETUNIT(a6)
- tst.l d0
- bne .End
- lea DiskResourcePort(A_DEVICE),a0
- move.l 4,a6
- SYS WaitPort
- bra .GetUnit
- .End:
-
- IFEQ TYPE-DISK
- move.l TDUnit(A_UNIT),d0
- beq .EndEnd
- move.l d0,a0
- move.w TDU_CURRTRK(a0),TDU_CURRTRK(A_UNIT)
- ENDC
-
- .EndEnd: movem.l (sp)+,d0-d1/a0-a2/a6
- ; PUTDEBUG <'GetDrive: Done'>
- rts
-
- FreeDrive:
- ;Release this unit to other users of disk.resource.
-
- movem.l d0-d1/a0-a1/a6,-(sp)
-
- IFEQ TYPE-DISK
- move.l TDUnit(A_UNIT),d0
- beq .Skip
- move.l d0,a0
- move.w TDU_CURRTRK(A_UNIT),TDU_CURRTRK(a0)
- .Skip:
- ENDC
-
- move.l DiskResourceBase(A_DEVICE),a6
- jsr DR_GIVEUNIT(a6)
- movem.l (sp)+,d0-d1/a0-a1/a6
- rts
-
- ;***************************** Low-level disk code **************************
-
- ;Standard register usage:
- ;A6 - $dff000
- ;A5 (A_DEVICE) - device ptr
- ;A4 (A_UNIT) - unit ptr
- ;A3 (A_IO) - pointer to IO request
-
- ;For reading/writing:
- ;D0.L - length (bytes)
- ;D1.L - offset (bytes)
- ;A0.L - buffer
-
- ;NOTE: Error code returned in D7.L - 0 if ok, else error
-
- ;General errors
- DISK_BadParameter equ TDERR_NotSpecified
-
- ;Read errors
- DISK_NoSync equ TDERR_NoSecHdr
- DISK_BadHeader equ TDERR_BadHdrSum
- DISK_BadData equ TDERR_BadSecSum
-
- ;Write errors
- DISK_WriteProtected equ TDERR_WriteProt
- DISK_VerifyError equ TDERR_NotSpecified
-
- IFND dskpt
- dskpt equ dskpth
- ENDC
-
- ;Note: This is a public value, but it is NOT meant to be changed!!!
- DISK_RawBufSize equ 13630
-
- ciabprb equ $bfd100
- ciaapra equ $bfe001
- ciabddrb equ $bfd300
- ciaaddra equ $bfe201
-
- ***************************************************************
- ;Important disk parameters, summary:
-
- ;Step rate: 3ms. 4ms when looking for track zero.
- ;Settle time: 15ms
- ;Post-write delay: 3ms (officially 1.3ms)
- ;Side select delay: 2ms (officially 1.3ms)
- ***************************************************************
-
- DISK_Read:
- movem.l d0-d5/a0/a1,-(sp)
-
- move.l a0,a1 ;destination ptr in A1
- move.l d0,d5
-
- ;The meat of the read routine...
-
- .ReadLoop:
- move.l d1,d3
- divu.w d6,d3 ;d3.w is track #
- move.l d3,d4
- clr.w d4
- swap d4 ;d4.l is byte offset into track
- bsr MinSeek
- tst.l d4 ;any offset?
- bne .Complex ;yes
- cmp.l d6,d5 ;check remaining length
- blo .Complex ;if not a track, forget it
- move.l a1,d0
- btst #0,d0 ;word aligned?
- bne .Complex ;no, do it the long way
- move.l a1,a0
- bsr ReadTrackAndDecodeNoBuffer
- tst.l d7
- bne .End
- sub.l d6,d5 ;update length
- beq .End
- add.l d6,a1 ;update destination pointer
- add.l d6,d1 ;update offset
- bra .ReadLoop
-
- .Complex:
- move.l DecodedBuffer(A_DEVICE),a0
- bsr ReadTrackAndDecodeBuffered
- tst.l d7
- bne .End
- add.l d4,a0
- sub.l d6,d4
- neg.l d4
- add.l d4,d1 ;update offset
-
- ;D4.L - number of bytes that could be transferred from this track
- ;D5.L - number of bytes left in the entire Read request
-
- cmp.l d5,d4
- bhs .FinishUp
- sub.l d4,d5 ;update length
- move.l d4,d0
- bsr CopyMem
- add.l d0,a1 ;update destination pointer
- bra .ReadLoop
- .FinishUp:
- move.l d5,d0
- bsr CopyMem ;use number of bytes left in entire request
- .End: movem.l (sp)+,d0-d5/a0/a1
- rts
-
- SelectDrive:
- ;Selects drive AND turns on motor
- ;Selects UnitNum(A_UNIT).
-
- movem.l d0/d2,-(sp)
- move.b UnitNum(A_UNIT),d2
- bsr Deselect
- bclr #CIAB_DSKMOTOR,ciabprb ;motor on
- move.l d2,d0
- addq.b #3,d0
- bclr d0,ciabprb ;select drive X
- bset d2,MotorState(A_DEVICE)
-
- moveq #4,d2
- .Wait: btst #CIAB_DSKRDY,ciaapra
- beq .End
- move.l #100*1000,d0
- bsr delay
- dbra d2,.Wait
-
- .End: movem.l (sp)+,d0/d2
- rts
-
- Deselect: or.b #CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3,ciabprb ;Deselect all drives
- rts
-
- SelectDriveSameMotor:
- ;Select a drive WITHOUT changing the current motor state
- ;Selects UnitNum(A_UNIT).
-
- push d2
- move.b UnitNum(A_UNIT),d2
- bsr Deselect
- bclr #CIAB_DSKMOTOR,ciabprb ;motor on
- btst d2,MotorState(A_DEVICE)
- bne .WasOn
- bset #CIAB_DSKMOTOR,ciabprb ;motor off
- .WasOn: addq.b #3,d2
- bclr d2,ciabprb ;select drive X
- pop d2
- _RTS: rts
-
- DISK_Update:
- ;Flush track buffer if "dirty"
-
- bclr #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
- beq _RTS
- movem.l d0-d2/a0/a2/a6,-(sp)
- move.l #_custom,a6
-
- ;This is somewhat tricky, because the currently selected drive may not be
- ;the drive that we want to write to!
- move.b UnitNum(A_UNIT),d0
- move.w TDU_CURRTRK(A_UNIT),d1
- move.b BufferDrive(A_DEVICE),UnitNum(A_UNIT) ;fake drive
- move.b BufferTrack(A_DEVICE),TDU_CURRTRK+1(A_UNIT) ;fake track
- bsr SelectDrive
- bsr SelectSide
-
- ;The logic of this routine is complicated by the need to support the write
- ;optimization scheme (see DISK_Write for details). What we need to do is
- ;check the WriteMap to determine whether a ReadTrackAndDecode is required
- ;before finally writing...
- move.l WriteMap(A_DEVICE),d2
- and.l #%11111111111,d2 ;changes for different drive types!
- cmp.l #%11111111111,d2 ;changes for different drive types!
- beq .SkipRead
- move.l DecodedBuffer(A_DEVICE),a0
- bsr ReadTrackAndDecode
- tst.l d7
- bne .End
- .SkipRead:
- move.l d7,WriteMap(A_DEVICE)
-
- move.l DecodedBuffer(A_DEVICE),a2
- bsr EncodeAndWriteTrack
- move.b d0,UnitNum(A_UNIT)
- move.w d1,TDU_CURRTRK(A_UNIT)
- bsr SelectDrive
- .End: movem.l (sp)+,d0-d2/a0/a2/a6
- rts
-
- SelectSide:
- ;Select the correct side based on current track. This routine MUST be
- ;called by certain routines (e.g. Update, Write) because the side select is
- ;lost after a GiveUnit!
- bclr #CIAB_DSKSIDE,ciabprb ;set to upper
- btst #0,TDU_CURRTRK+1(A_UNIT)
- bne .skip
- bset #CIAB_DSKSIDE,ciabprb ;set to lower
- .skip: push d0
- move.l #2*1000,d0
- bsr delay
- pop d0
- rts
-
- ;Enter with destination track in d3.b
-
- ;Note: This routine checks for valid track numbers -- if either the
- ;current track number or the destination is invalid, PANIC.
-
- MinSeek:
- ;Only call MinSeek if it is certain that the side select has not been
- ;changed!
- cmp.b TDU_CURRTRK+1(A_UNIT),d3
- bne Seek
- move.w d0,-(sp)
- move.b BufferDrive(A_DEVICE),d0
- cmp.b UnitNum(A_UNIT),d0
- bne .Seek
- move.w (sp)+,d0
- rts
- .Seek: move.w (sp)+,d0
-
- Seek: movem.l d0/d3-d4,-(sp)
-
- bsr DISK_Update
- tst.l d7
- bne .End
-
- move.l TDU_STEPDELAY(A_UNIT),d0
- cmp.b #160,d3
- bhs .Error
-
- ;Set head
- bclr #CIAB_DSKSIDE,ciabprb ;set to upper
- btst #0,d3
- bne .skip
- bset #CIAB_DSKSIDE,ciabprb ;set to lower
- .skip:
- move.w TDU_CURRTRK(A_UNIT),d4
- lsr.b #1,d4
- cmp.b #80,d4
- bhs .Error
- move.b d3,TDU_CURRTRK+1(A_UNIT)
- lsr.b #1,d3
- bset #CIAB_DSKDIREC,ciabprb ;set to "out" (lower tracks)
- sub.b d3,d4
- beq .SideSelectOnly
- bhi .StepIn ;Go if DISK_CurrentTrack > Destination
- bclr #CIAB_DSKDIREC,ciabprb ;set to "in" (higher tracks)
- neg.b d4
- .StepIn:
- bsr SelectDriveSameMotor
- bset #CIAB_DSKSTEP,ciabprb
- bclr #CIAB_DSKSTEP,ciabprb ;step head
- bset #CIAB_DSKSTEP,ciabprb
- bsr Deselect
- bsr delay
- subq.b #1,d4
- bne .StepIn
- move.l TDU_SETTLEDELAY(A_UNIT),d0
- bsr delay
- bsr SelectDriveSameMotor
- .End: movem.l (sp)+,d0/d3-d4
- rts
- .SideSelectOnly:
- move.l #2*1000,d0
- bsr delay
- bra .End
- .Error:
- ; move.w #$f00,$dff180
- bra .Error
-
- ;*************************** Delay code ****************************
-
- delay:
-
- ;Enter with microseconds in D0.L
-
- movem.l d0-d1/a0-a1/a6,-(sp)
- lea TimerIORequest(A_DEVICE),a1
- move.w #TR_ADDREQUEST,IO_COMMAND(a1)
- clr.l TV_SECS+IO_SIZE(a1)
- move.l d0,TV_MICRO+IO_SIZE(a1)
- move.l 4,a6
- SYS DoIO
-
- ;Clear signal bit (TimeOutWait depends on the signal bit being 'correct').
- lea TimerIORequest(A_DEVICE),a1
- move.l MN_REPLYPORT(a1),a0
- move.b MP_SIGBIT(a0),d0
- moveq #0,d1
- bset d0,d1
- moveq #0,d0
- SYS SetSignal
- movem.l (sp)+,d0-d1/a0-a1/a6
- rts
-
- TimeOutWait:
-
- ;Wait for signal mask in D0, but include a time-out specified in D1 (usecs).
- ;Returns wait mask in D0, or 0 if a time-out occured (Z flag will be set).
-
- movem.l d1-d4/a0-a2/a6,-(sp)
- move.l d0,d3
- move.l 4,a6
- lea TimerIORequest(A_DEVICE),a1
- move.l a1,a2
- move.w #TR_ADDREQUEST,IO_COMMAND(a1)
- clr.l TV_SECS+IO_SIZE(a1)
- move.l d1,TV_MICRO+IO_SIZE(a1)
- move.l MN_REPLYPORT(a1),a0
- moveq #0,d2
- move.b MP_SIGBIT(a0),d1
- bset d1,d2
- SYS SendIO
- move.l d3,d0
- add.l d2,d0 ;equivilent to OR in this case
- SYS Wait
- and.l d3,d0 ;exclude timer signal
- move.l d0,d4
- beq .TimeOut ;if zero, timer signal was the only signal
- move.l a2,a1
- SYS AbortIO
- .TimeOut: move.l a2,a1
- SYS WaitIO
- moveq #0,d0 ;value
- move.l d2,d1 ;mask (timer sig)
- SYS SetSignal ;make SURE the timer signal is clear
- move.l d4,d0
- movem.l (sp)+,d1-d4/a0-a2/a6
- rts
-
- ;*************************** End delay code ****************************
-
- ;*************************** Interrupt routines ************************
-
- ;------------------------------------------
- ;Register contents upon entering a handler:
- ;D1 - (INTENAR) AND (INTREQR)
- ;A0 - $dff000
- ;A1 - data ptr (device ptr in this case)
- ;A6 - ExecBase
-
- ;d0, d1, a0, a1, a5, and a6 are scratch.
- ;-----------------------------------------
-
- ;However, the disk.resource autodoc indicates the following arrangement for
- ;interrupts installed by GetUnit:
-
- ;D0/D1/A0/A1 are scratch
- ;A1 points to IS_DATA (device pointer in this case).
- ;Make no other assumptions!
-
- SyncInt: addq.w #1,SyncCount(a1)
- move.l SyncSig(a1),d0
- lea Task(a1),a1
- push a6
- move.l 4,a6
- SYS Signal
- pop a6
-
- ;Delay approximately 63us to avoid two sync interrupts
- ;This could be improved.
- moveq #1,d0
- .L1: move.b vhposr+_custom,d1
- .L2: cmp.b vhposr+_custom,d1
- beq .L2
- dbra d0,.L1
- move.w #INTF_DSKSYNC,intreq+_custom ;clear sync
- rts
-
- BlockInt:
- push a6
- move.l #_custom,a6
- move.w #INTF_DSKBLK,intreq(a6)
-
- ;Test DEVB_Verify flag. If set, initiate a read into the verify buffer.
- bclr #DEVB_Verify,DEV_FLAGS(a1)
- beq .NoVerify
-
- move.w #INTF_SETCLR+INTF_DSKSYNC,intena(a6) ;enable sync int
- move.l VerifyBuffer(a1),dskpt(a6)
- move.w #ADKF_SETCLR+ADKF_WORDSYNC,adkcon(a6)
- move.w #0,dsklen(a6)
- move.w #$9761,dsklen(a6)
- move.w #$9761,dsklen(a6)
- move.w #$4489,dsksync(a6) ;set magic sync word
- ;Now the first sync interrupt we get will be known to be valid, because the
- ;DMA read is fully started.
- bra .End
-
- .NoVerify:
- move.l BlockSig(a1),d0
- lea Task(a1),a1
- move.l 4,a6
- SYS Signal
- .End: pop a6
- rts
-
- IndexInt:
- move.w IndexDskLen(a1),d0
- beq .End
- move.w d0,dsklen+_custom
- move.w d0,dsklen+_custom
- clr.w IndexDskLen(a1)
- .End: rts
-
- EnableIndex:
- movem.l d0-d1/a0-a1/a6,-(sp)
- move.l CIABase(A_DEVICE),a6
- moveq #16,d0
- SYS SetICR ;clear interrupt
- move.l #16+128,d0
- SYS AbleICR ;enable interrupt
- movem.l (sp)+,d0-d1/a0-a1/a6
- rts
- DisableIndex:
- movem.l d0-d1/a0-a1/a6,-(sp)
- move.l CIABase(A_DEVICE),a6
- moveq #16,d0
- SYS AbleICR ;disable interrupt
- moveq #16,d0
- SYS SetICR ;clear interrupt
- movem.l (sp)+,d0-d1/a0-a1/a6
- rts
-
- ;*********************** End interrupt routines ************************
-
- ClearSigs:
- movem.l d0-d1/a0-a1/a6,-(sp)
- move.w #INTF_DSKBLK+INTF_DSKSYNC,intreq+_custom ;clear sync+done
- clr.w SyncCount(A_DEVICE)
- clr.w IndexDskLen(A_DEVICE)
- move.l 4,a6
- moveq #0,d0 ;value
- move.l SyncSig(A_DEVICE),d1
- add.l BlockSig(A_DEVICE),d1
- SYS SetSignal
- movem.l (sp)+,d0-d1/a0-a1/a6
- rts
-
- SetRegs:
- ;This routine enables the block interrupt, but leaves sync interrupts
- ;disabled.
-
- move.l RawBuffer(A_DEVICE),dskpt(a6)
- move.w #ADKF_MSBSYNC+ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6)
- move.w #ADKF_SETCLR+ADKF_FAST+ADKF_WORDSYNC+ADKF_MFMPREC,adkcon(a6)
- move.w #DMAF_SETCLR+DMAF_MASTER+DMAF_DISK,dmacon(a6) ;enable disk DMA
- bsr ClearSigs
- move.w #INTF_SETCLR+INTF_DSKBLK,intena(a6) ;enable block int
- rts
-
- PreComp:
- ;Enter with $dff000 in A6
- movem.l d0-d1,-(sp)
- move.w #ADKF_PRECOMP0+ADKF_PRECOMP1,adkcon(a6) ;no precomp
- move.w TDU_CURRTRK(A_UNIT),d0
- cmp.w TDU_COMP01TRACK(A_UNIT),d0
- bls .End
- move.w #ADKF_SETCLR+ADKF_PRECOMP0,d1
- cmp.w TDU_COMP10TRACK(A_UNIT),d0
- bls .Done
- move.w #ADKF_SETCLR+ADKF_PRECOMP1,d1
- cmp.w TDU_COMP11TRACK(A_UNIT),d0
- bls .Done
- move.w #ADKF_SETCLR+ADKF_PRECOMP0+ADKF_PRECOMP1,d1
- .Done: move.w d1,adkcon(a6)
- .End: movem.l (sp)+,d0-d1
- rts
-
- ScanSync:
- ;Scan for sync mark
- ;A2 - pointer to raw data -- updated
- ;Error code in D7
-
- bsr WaitWordSync
- tst.l d7
- bne .End
-
- ;This routine is trickier than it appears. The trick is that we must NOT
- ;assume a $4489 at the beginning of our buffer. This phenomenon occurs when
- ;the DMA starts in the middle of the first sync word. The second sync word
- ;is thrown away by the hardware. It sounds exotic, but it actually happens
- ;quite often!
-
- push a4
- move.l RawBuffer(A_DEVICE),a4
- cmp.l a4,a2
- add.w #DISK_RawBufSize,a4 ;does not affect flags
- beq .found ;if start of buffer, don't scan for sync!!!
-
- .Sync: cmpi.w #$4489,(a2)+
- beq .found
- cmp.l a2,a4
- bhi .Sync
- .Error: pop a4
- moveq #DISK_NoSync,d7
- rts
- .found: cmpi.w #$4489,(a2)
- bne .ok
- addq.l #2,a2
- cmp.l a2,a4
- bhi .found
- bra .Error
- .ok: pop a4
- .End: rts
-
- StopDMA: move.w #0,dsklen(a6)
- move.w #1<<15,dsklen(a6)
- move.w #1<<15,dsklen(a6) ;zero-length DMA transfer
- move.w #INTF_DSKBLK+INTF_DSKSYNC,intena(a6) ;disable ints
- bra ClearSigs
-
- WaitWordSync:
- ;Wait for a sync mark or disk block done
- ;Will return an error in 300ms if nothing happens.
-
- movem.l d0-d1,-(sp)
- .Wait: tst.w SyncCount(A_DEVICE)
- bne .Sync
- move.l SyncSig(A_DEVICE),d0
- add.l BlockSig(A_DEVICE),d0
- move.l #300*1000,d1 ;time-out (300ms)
- bsr TimeOutWait
- beq .Error
- and.l BlockSig(A_DEVICE),d0
- bne .Done
- bra .Wait
- .Sync: subq.w #1,SyncCount(A_DEVICE)
- .End: movem.l (sp)+,d0-d1
- rts
- .Done: move.w #$C000,SyncCount(A_DEVICE) ;this is obscure - should change
- bra .End
- .Error: moveq #DISK_NoSync,d7
- bra .End
-
- ReadTrackAndDecodeBuffered:
- push d0
- move.l WriteMap(A_DEVICE),d0
- beq .OK ;no optimized writes to worry about
- and.l #%11111111111,d0 ;changes for different drive types!
- cmp.l #%11111111111,d0 ;changes for different drive types!
- beq .End
- moveq #-1,d0
- move.l d0,WriteMap(A_DEVICE) ;mark sectors as filled
- bra .ReadTrack
-
- .OK: move.b UnitNum(A_UNIT),d0
- cmp.b BufferDrive(A_DEVICE),d0
- bne .ReadTrack
- move.w TDU_CURRTRK(A_UNIT),d0
- cmp.b BufferTrack(A_DEVICE),d0
- bne .ReadTrack
- .End: pop d0
- rts
- .ReadTrack:
- pop d0
-
- ReadTrackAndDecode:
-
- ;Input: Buffer ptr in A0
- ;Returns error code in D7
-
- move.b UnitNum(A_UNIT),BufferDrive(A_DEVICE)
- move.b TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
-
- ReadTrackAndDecodeNoBuffer:
- move.w d0,-(sp)
- move.b TDU_RETRYCNT(A_UNIT),d0
- .Retry: moveq #0,d7
- bsr ReadTrackAndDecodeNoRetry
- tst.l d7
- beq .End
- subq.b #1,d0
- bpl .Retry
- .End: move.w (sp)+,d0
- rts
-
- ReadTrackAndDecodeNoRetry:
- movem.l d0-d6/a0-a2,-(sp)
-
- ;Initiate a raw track read
- ;Because we are using WORDSYNC interrupts, this code is VERY VERY tricky!!!!!
-
- bsr SetRegs
- move.w #0,dsksync(a6) ;set invalid sync word
- ;At this point sync interrupts will stop happenning, because $0000 will
- ;never occur. So, we clear the interrupt and enable it.
- bsr ClearSigs
- move.w #INTF_SETCLR+INTF_DSKSYNC,intena(a6) ;enable sync int
- move.w #0,dsklen(a6)
- move.w #$9A9E,dsklen(a6)
- move.w #$9A9E,dsklen(a6) ;read approx. 13,628 bytes
- move.w #$4489,dsksync(a6) ;set magic sync word
-
- ;Now the first sync interrupt we get will be known to be valid, because the
- ;DMA read is fully started.
-
- ;Decode track
- move.l RawBuffer(A_DEVICE),a2
-
- moveq #11-1,d3 ;# of sectors
- move.l #$55555555,d2
-
- ;Wait for first sync marks...
- bsr WaitWordSync
- tst.l d7
- bne .End
-
- .SecLoop:
- bsr ScanSync
- tst.l d7
- bne .End
-
- bsr DecodeLong ;get header
- subq.l #8,a2
-
- and.w #$ff00,d0 ;mask off sector number
- move.l d0,d1
- cmp.w #$0a00,d1
- bhi .HeaderError
- add.w d1,d1 ;convert to sector offset
- lsr.w #4,d0 ;get sector label offset
- lea SectorLabels(A_DEVICE),a1
- lea (a1,d0.w),a1
-
- ;This code supports the write optimization...
- lsr.w #4,d0 ;sector number
- move.l WriteMap(A_DEVICE),d4
- btst d0,d4 ;should we avoid reading this sector?
- bne .EndLoop ;yes, skip to next sector
-
- move.l d1,-(sp)
- moveq #0,d5
- move.l (a2)+,d0
- eor.l d0,d5
- move.l (a2)+,d0
- eor.l d0,d5
- ;Decode and checksum sector label
- moveq #3,d6
- .Label: move.l 16(a2),d4
- eor.l d4,d5
- and.l d2,d4
- move.l (a2)+,d1
- eor.l d1,d5
- and.l d2,d1
- add.l d1,d1
- or.l d1,d4
- move.l d4,(a1)+
- dbra d6,.Label
- and.l d2,d5
- lea 16(a2),a2 ;point at header checksum
- move.l (sp)+,d1
- bsr DecodeLong ;header checksum
- cmp.l d0,d5
- bne .HeaderError
-
- ;Verify track position
- swap d1
- cmp.b TDU_CURRTRK+1(A_UNIT),d1
- bne .WrongTrack
- swap d1
-
- bsr DecodeLong ;data area checksum
- lea (a0,d1.w),a1 ;compute destination
-
- ;Decode and checksum data block
- moveq #127,d6
- moveq #0,d5
- .L1: move.l 512(a2),d4
- eor.l d4,d5
- and.l d2,d4
- move.l (a2)+,d1
- eor.l d1,d5
- and.l d2,d1
- add.l d1,d1
- or.l d1,d4
- move.l d4,(a1)+
- dbra d6,.L1
- and.l d2,d5
- lea 512(a2),a2
-
- cmp.l d0,d5 ;check data area checksum
- bne .DataError
- .EndLoop: dbra d3,.SecLoop
-
- .End: bsr StopDMA
- movem.l (sp)+,d0-d6/a0-a2
- rts
- .HeaderError:
- bsr Clear
- moveq #DISK_BadHeader,d7
- bra .End
- .DataError:
- bsr Clear
- moveq #DISK_BadData,d7
- bra .End
- .WrongTrack:
- bsr Clear
- moveq #TDERR_SeekError,d7
- move.w TDU_CURRTRK(A_UNIT),d3
- bsr SeekZero
- bsr SelectDrive
- bsr Seek
- bra .End
-
- DecodeLong:
- ;A2 - ptr to buffer -- updated
- ;D2 - $55555555
- ;D0 - result
-
- move.l d1,-(sp)
- move.l (a2)+,d0
- move.l (a2)+,d1
- and.l d2,d0
- and.l d2,d1
- add.l d0,d0 ;was lsl.l #1,d0
- or.l d1,d0
- move.l (sp)+,d1
- rts
-
- EncodeLong:
- ;Enter with data to be encoded in D0.L
- ;and pointer to destination in A0 -- updated
- ;Exit with checksum in D5
-
- movem.l d0-d4,-(sp)
- moveq #0,d5
- move.l #$55555555,d4
- move.l d0,d3
- lsr.l #1,d0
- bsr Encode
- move.l d3,d0
- bsr Encode
- and.l #$55555555,d5
- movem.l (sp)+,d0-d4
- rts
-
- Encode:
- ;Enter with longword to code in D0.L and #$55555555 in D4
- ;uses d0,d1,d2,a0 -- not saved
-
- ;Accumulates checksum in D5
-
- and.l d4,d0
- move.l d0,d2
- eor.l d4,d2
- move.l d2,d1
- add.l d2,d2
- lsr.l #1,d1
- bset #31,d1
- and.l d2,d1
- or.l d1,d0
- btst #0,-1(a0)
- beq .ok
- bclr #31,d0
- .ok: eor.l d0,d5
- move.l d0,(a0)+
- rts
-
- EncodeBlock:
- ;Destination is always chip RAM (RawBuffer).
- ;Source could be in chip RAM or fast RAM (in A2).
-
- movem.l d0-d1/a0-a1/a6,-(sp)
- move.l 4,a6
- move.l a2,a1
- SYS TypeOfMem
- and.l #MEMF_CHIP,d0
- bne .Chip
- movem.l (sp)+,d0-d1/a0-a1/a6
- bra EncodeBlockCPU
- .Chip: movem.l (sp)+,d0-d1/a0-a1/a6
- bra EncodeBlockBlit
-
- EncodeBlockCPU:
- ;Enter with pointer to source data in A2 -- updated
- ;Enter with pointer to destination in A0 -- updated
-
- ;Exit with checksum in D5
- move.l d6,-(sp)
- moveq #0,d5
- move.w #(512/4)-1,d6
-
- EncodeBlockSub:
- ;Number of longwords to encode (minus one) in D6.w
- movem.l d0-d4,-(sp)
-
- ;Encode odd bits
- push a2
- move.w d6,d3
- move.l #$55555555,d4
- .L1: move.l (a2)+,d0
- lsr.l #1,d0
- bsr Encode
- dbra d3,.L1
-
- ;Encode even bits
- pop a2
- move.w d6,d3
- .L2: move.l (a2)+,d0
- bsr Encode
- dbra d3,.L2
- and.l #$55555555,d5
- movem.l (sp)+,d0-d4
- move.l (sp)+,d6
- rts
-
- EncodeSectorLabels:
- ;D5 (checksum) must be initialized by caller
- move.l d6,-(sp)
- move.w #(16/4)-1,d6
- bra EncodeBlockSub
-
- EncodeBlockBlit:
- ;Enter with pointer to source data in A2 -- updated
- ;Enter with pointer to destination in A0 -- updated
-
- ;Exit with checksum in D5
-
- ;Extra blitter equates...
- BLTCPTR equ $048
- BLTBPTR equ $04c
- BLTAPTR equ $050
- BLTDPTR equ $054
-
- movem.l d0-d2/a0-a1/a6,-(sp)
- push a0
- move.l GraphBase(A_DEVICE),a6
- SYS OwnBlitter
- move.l (sp),a0
- move.l #_custom,a1
-
- move.w #$808,d0 ;BLTSIZE
-
- SYS WaitBlit
-
- move.w #$ffff,BLTAFWM(a1)
- move.w #$ffff,BLTALWM(a1)
- clr.w BLTBMOD(a1)
- clr.w BLTAMOD(a1)
- clr.w BLTDMOD(a1)
- move.w #$5555,BLTCDAT(a1)
-
- move.l a2,BLTBPTR(a1)
- move.l a2,BLTAPTR(a1)
- move.l a0,BLTDPTR(a1)
- move.w #$1db1,BLTCON0(a1)
- clr.w BLTCON1(a1)
- move.w d0,BLTSIZE(a1)
-
- SYS WaitBlit
-
- move.l a0,BLTBPTR(a1)
- move.l a2,BLTAPTR(a1)
- move.l a0,BLTDPTR(a1)
- move.w #$2d8c,BLTCON0(a1)
- move.w d0,BLTSIZE(a1)
- movem.l a0/a2,-(sp)
- lea 510(a2),a2 ;ptr to end of src
- lea 1022(a0),a0
-
- SYS WaitBlit
-
- move.l a2,BLTBPTR(a1) ;src end
- move.l a2,BLTAPTR(a1) ;src end
- move.l a0,BLTDPTR(a1) ;dst end
- move.w #$0db1,BLTCON0(a1)
- move.w #$1002,BLTCON1(a1) ;decrement
- move.w d0,BLTSIZE(a1)
- movem.l (sp)+,a0/a2
- lea 512(a0),a0
-
- SYS WaitBlit
-
- move.l a0,BLTBPTR(a1)
- move.l a2,BLTAPTR(a1)
- move.l a0,BLTDPTR(a1)
- move.w #$1d8c,BLTCON0(a1)
- clr.w BLTCON1(a1)
- move.w d0,BLTSIZE(a1)
- pop a0
-
- SYS WaitBlit
-
- bsr Correct
- lea 512(a0),a0
- bsr Correct
- lea -512(a0),a0
-
- move.w #(1024/4)-1,d0
- move.l #$55555555,d2
- moveq #0,d5
- .. move.l (a0)+,d1
- eor.l d1,d5
- dbra d0,..
- and.l d2,d5
-
- SYS DisownBlitter
- movem.l (sp)+,d0-d2/a0-a1/a6
- lea 512(a2),a2 ;update source pointer
- lea 1024(a0),a0 ;update destination pointer
- rts
-
- ;This routine corrects the high bit of the current byte based on the
- ;low bit of the previous byte.
-
- Correct:
- push d0
- move.b (a0),d0
- btst #0,-1(a0)
- bne .ResetClock
- btst #6,d0
- bne .end
- bset #7,d0
- bra .end1
- .ResetClock:
- bclr #7,d0
- .end1: move.b d0,(a0)
- .end: pop d0
- rts
-
- DISK_Wait:
- ;Assumes $dff000 in A6.
- movem.l d0-d1,-(sp)
- tst.w SyncCount(A_DEVICE)
- bmi .OK ;if WaitWordSync detected a BlockSig, don't wait!
- move.l BlockSig(A_DEVICE),d0
- move.l #300*1000,d1
- bsr TimeOutWait
- bne .OK
- moveq #DISK_NoSync,d7
- .OK: move.w #INTF_DSKBLK+INTF_DSKSYNC,intena(a6) ;disable ints
- bsr ClearSigs
- movem.l (sp)+,d0-d1
- rts
-
- EncodeAndWriteTrack:
- ;Enter with pointer to source data in A2
-
- movem.l d0-d6/a0-a2,-(sp)
-
- btst #CIAB_DSKPROT,ciaapra ;check write protect status
- beq .Protected
-
- move.l RawBuffer(A_DEVICE),a0
-
- ;Gap = 1660 bytes - 2 bytes for hardware bug
- move.l #$aaaaaaaa,d1 ;10101010...
- move.w #414,d0
- .. move.l d1,(a0)+
- dbra d0,..
- subq.l #2,a0 ;leave room for 2 extra bytes at the very end
-
- moveq #11,d1 ;number of sectors
- moveq #0,d3 ;sector count
- .SecLoop:
- move.l #$aaaaaaaa,(a0)
- bsr Correct
- addq.l #4,a0
- move.l #$44894489,(a0)+
- move.l #$ff000000,d0
- moveq #0,d6
- move.w TDU_CURRTRK(A_UNIT),d6
- swap d6
- or.l d6,d0
- move.l d3,d6
- lsl.l #8,d6
- or.l d6,d0
- or.l d1,d0
- bsr EncodeLong ;header
-
- ;Encode sector label
- push a2
- lea SectorLabels(A_DEVICE),a2
- move.l d3,d0
- lsl.l #4,d0 ;sector*16
- lea (a2,d0.w),a2
- bsr EncodeSectorLabels
- pop a2
-
- move.l d5,d0
- bsr EncodeLong ;header checksum
- move.l a0,d2 ;save raw data pointer
- addq.l #8,a0
- bsr EncodeBlock ;encode data block
- move.l d5,d0
- exg a0,d2
- bsr EncodeLong ;data block checksum
- bsr Correct
- move.l d2,a0
- addq.l #1,d3
- subq.l #1,d1
- bne .SecLoop
-
- move.w #$aaa8,(a0)
- bsr Correct ;extra word to avoid hardware bug
-
- ;Physically write the data
- .WriteAgain:
- bsr SetRegs
- bsr PreComp
- move.w #ADKF_WORDSYNC,adkcon(a6) ;turn OFF wordsync!!!
- move.w d7,dsksync(a6)
- bsr ClearSigs
-
- btst #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
- beq .SkipVerify
- bset #DEVB_Verify,DEV_FLAGS(A_DEVICE)
- .SkipVerify:
-
- move.l VerifyBuffer(A_DEVICE),a0
- clr.l (a0)+
- clr.l (a0)
- move.w #0,dsklen(a6)
- move.w #$DA9E,dsklen(a6)
- move.w #$DA9E,dsklen(a6) ;write approx. 13,628 bytes
-
- ;This piece of code (commented out) tests the function of the rare
- ;"interrupt delayed" requester. (I've never seen it appear in actual use).
- comment |
- ;TEST
- push a6
- move.l 4,a6
- SYS Disable
- move.w #5000,d0
- .L1: move.b vhposr+_custom,d1
- .L2: cmp.b vhposr+_custom,d1
- beq .L2
- dbra d0,.L1
- SYS Enable
- pop a6
- |
-
- btst #TDPB_VERIFY,TDU_PUBFLAGS(A_UNIT)
- beq .NoVerify
-
- ;VERIFY
-
- ;We verify by comparing the raw MFM data in RawBuffer (what we just wrote)
- ;and VerifyBuffer (what is coming in). Due to the ingenious method of
- ;verifying (thanks to Sebastiano Vigna!) the data comes in sector-by-sector
- ;in the same order that we wrote it.
-
- move.l VerifyBuffer(A_DEVICE),a0
- move.l RawBuffer(A_DEVICE),a2
- lea 1666(a2),a2 ;first sector (minus sync)
- bsr WaitWordSync
- tst.l d7
- bne .VerifyError
- bsr WaitWordSync
- tst.l d7
- bne .VerifyError
- ;Now we have our first sector in the verify buffer. Scan for a
- ;sync mark. (There may be 1 or 2 sync marks).
- cmp.w #$4489,(a0)+
- bne .VerifyError
- cmp.w #$4489,(a0)
- bne .HaveSync
- addq.l #2,a0
- .HaveSync:
-
- ;We go through a rather elaborate procedure here to make sure that we've
- ;started reading with sector 0. (If not, display a requester informing the
- ;user that something is locking out level-1 interrupts for a long period of
- ;time).
- push a2
- move.l #$55555555,d2
- move.l a0,a2
- bsr DecodeLong
- pop a2
- and.w #$ff00,d0
- beq .Sector0
- move.l a0,a1
- moveq #9,d0
- moveq #0,d1
- .. move.l (a1)+,d3
- eor.l d3,d1
- dbra d0,..
- and.l d2,d1
- push a2
- move.l a1,a2
- bsr DecodeLong
- pop a2
- cmp.l d0,d1
- bne .VerifyError
-
- ;Display an informational requester.
- bsr StopDMA
- push a6
- move.l IntBase(A_DEVICE),a6
- cmp.w #36,LIB_VERSION(a6)
- bhi .KS20
-
- ;Running under 1.3. Put up a DisplayAlert.
- moveq #0,d0 ;alert type (recoverable)
- lea .AlertLockOut(pc),a0
- moveq #20,d1 ;height
- SYS DisplayAlert
- pop a6
- bra .WriteAgain
-
- ;Running under 2.0. Put up a EasyRequest.
- .KS20:
- sub.l a0,a0
- sub.l a2,a2
- lea .LockOut(pc),a1
- SYS EasyRequestArgs
- pop a6
- bra .WriteAgain
-
- ;Note: Probably should compare with the blitter, but this will be fairly
- ;fast.
-
- .Sector0:
- move.w #270-1,d0
- .. cmp.l (a0)+,(a2)+
- dbne d0,..
- bne .VerifyError
-
- ;Now we are over the initial hump of the first sync mark. The rest of the
- ;compare is even easier.
- moveq #9-1,d1
- .VLoop: bsr WaitWordSync
- tst.l d7
- bne .VerifyError
- move.w #272-1,d0
- .. cmp.l (a0)+,(a2)+
- dbne d0,..
- bne .VerifyError
- dbra d1,.VLoop
-
- ;We have one more sector to verify. This time we must wait for "Block
- ;done", rather than another sync.
-
- bsr DISK_Wait
- tst.l d7
- bne .VerifyError
- move.w #272-1,d0
- .. cmp.l (a0)+,(a2)+
- dbne d0,..
- bne .VerifyError
- bra .End
-
- .VerifyError:
- moveq #0,d7 ;don't propagate the error to the app
- ;We go here if an error is detected during the verify. We first shut down
- ;the read operation that may be in progress, then put up a requester and let
- ;the user choose whether to retry or abort.
-
- bsr StopDMA ;stop!!
- push a6
- move.l IntBase(A_DEVICE),a6
- cmp.w #36,LIB_VERSION(a6)
- bhi .DoKS20
-
- ;Running under 1.3. Put up a DisplayAlert.
- moveq #0,d0 ;alert type (recoverable)
- lea .AlertVError(pc),a0
- moveq #20,d1 ;height
- SYS DisplayAlert
- ;D0 is set to 'TRUE' if the LEFT button was pressed.
- pop a6
- tst.l d0
- bne .WriteAgain ;go if left button pressed
- bra .End
-
- ;Running under 2.0. Put up a EasyRequest.
- .DoKS20: sub.l a0,a0
- sub.l a2,a2
- lea .VError(pc),a1
- SYS EasyRequestArgs
- pop a6
- tst.l d0
- bne .WriteAgain ;go if left gadget hit
- bra .End
- .NoVerify:
- bsr DISK_Wait
- move.l #3*1000,d0
- bsr delay ;post-write delay
- .End: movem.l (sp)+,d0-d6/a0-a2
- rts
- .Protected:
- moveq #DISK_WriteProtected,d7
- bra .End
-
- .AlertVError:
- dc.w 10 ;x coordinate
- dc.b 10 ;y coordinate
- dc.b '*** VERIFY ERROR !!! *** Hit LEFT button to RETRY'
- dc.b ', or RIGHT button to CANCEL.',0
- dc.b 0 ;continuation byte
- even
-
- .AlertLockOut:
- dc.w 10 ;x coordinate
- dc.b 10 ;y coordinate
- dc.b 'Disk block interrupt delayed by >10ms.'
- dc.b ' Press mouse button.',0
- dc.b 0 ;continuation byte
- even
-
- .VError: dc.l es_SIZEOF
- dc.l 0
- dc.l .Title
- dc.l .MainText
- dc.l .GadgetText
- .LockOut: dc.l es_SIZEOF
- dc.l 0
- dc.l .Title
- dc.l .LockOutTxt
- dc.l .Okay
- .LockOutTxt:
- dc.b 'Disk block interrupt delayed by >10ms caused erroneous'
- dc.b ' verify.',0
- .Okay: dc.b 'If you say so. Try it again!',0
- .Title: dc.b 'hackdisk.device message',0
- .MainText:
- dc.b '*** VERIFY ERROR!!! ***',0
- .GadgetText:
- dc.b 'Retry|Cancel',0
- even
-
- DISK_Write:
-
- ;Error code returned in D7, as always.
-
- movem.l d0-d5/a0-a2,-(sp)
-
- btst #UNITB_WriteProtected,UNIT_FLAGS(A_UNIT)
- bne .ProtError
-
- move.l d0,d5 ;length
-
- ;The meat of the write routine...
-
- .WriteLoop:
- move.l d1,d3 ;offset
- divu.w d6,d3 ;d3.w is track #
- move.l d3,d4
- clr.w d4
- swap d4 ;d4.l is byte offset into track
- bsr MinSeek
- tst.l d4 ;any offset?
- bne .Complex ;yes
- cmp.l d6,d5 ;at least a track left?
- blo .Complex ;no
- move.l a0,d0
- btst #0,d0 ;word aligned?
- bne .Complex ;no, do it the long way
- move.l a0,a2
- bsr EncodeAndWriteTrack
- tst.l d7
- bne .End
- sub.l d6,d5 ;update length
- beq .End
- add.l d6,a0 ;update source pointer
- add.l d6,d1 ;update offset
- bra .WriteLoop
-
- .Complex:
-
- ;This part is somewhat difficult. We check the offset and length parameters
- ;to see whether they're a multiple of 512. If so, we keep track of which
- ;sectors will be written in the buffer. This information is later used by
- ;Update to determine whether a part of the original track must be read in.
- ;(We don't attempt this optimization if the user is writing some odd number
- ;of bytes...This is probably why trackdisk has the limits that it does).
-
- tst.l d5
- beq .End ;nothing left, forget it
- move.l d4,d0
- and.w #%111111111,d0
- bne .NoOpt
- move.l d4,d0
- move.l d5,d2
- and.w #%111111111,d2
- bne .NoOpt
- move.l d5,d2
- lsr.l #8,d0
- lsr.l #1,d0 ;get starting sector number
- lsr.l #8,d2
- lsr.l #1,d2 ;get length in sectors
- move.l WriteMap(A_DEVICE),d7
- .OptLoop: bset d0,d7
- addq.b #1,d0
- cmp.b #32,d0
- beq .EOpt
- subq.l #1,d2
- bne .OptLoop
- .EOpt: move.l d7,WriteMap(A_DEVICE)
- moveq #0,d7
-
- ;This is normally done by ReadTrackAndDecode.
- move.b UnitNum(A_UNIT),BufferDrive(A_DEVICE)
- move.b TDU_CURRTRK+1(A_UNIT),BufferTrack(A_DEVICE)
- bra .Opt ;don't read (yet)
-
- .NoOpt: push a0
- move.l DecodedBuffer(A_DEVICE),a0
- bsr ReadTrackAndDecodeBuffered
- pop a0
- tst.l d7
- bne .End
- .Opt: move.l DecodedBuffer(A_DEVICE),a1
- add.l d4,a1 ;add byte offset into track
- sub.l d6,d4
- neg.l d4
- add.l d4,d1 ;update offset
-
- ;D4.L - number of bytes that could be transferred from this track
- ;D5.L - number of bytes left in the entire Read request
-
- cmp.l d5,d4
- bhs .FinalWrite
- sub.l d4,d5 ;update length
- move.l d4,d0
- bsr CopyMem
- add.l d0,a0 ;update dest pointer
- bset #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
- bra .WriteLoop
-
- .FinalWrite:
- move.l d5,d0
- bsr CopyMem
- bset #DEVB_Dirty,DEV_FLAGS(A_DEVICE)
- .End: movem.l (sp)+,d0-d5/a0-a2
- rts
- .ProtError:
- moveq #DISK_WriteProtected,d7
- bra .End
-
- MotorOff:
- ;MotorOff turns off all drive motors and leaves all drives deselected
- bsr Deselect
- bset #CIAB_DSKMOTOR,ciabprb ;motor off
- and.b #~(CIAF_DSKSEL0+CIAF_DSKSEL1+CIAF_DSKSEL2+CIAF_DSKSEL3),ciabprb ;select all drives
- bsr Deselect
- clr.b MotorState(A_DEVICE)
- rts
-
- Inquire:
- IFND _LVOGetUnitID
- _LVOGetUnitID equ -30
- ENDC
- movem.l d0-d2/a0-a1/a6,-(sp)
- moveq #0,d2
- move.l DiskResourceBase(A_DEVICE),a6
- .Loop: move.l d2,d0
- SYS GetUnitID
- tst.l d0
- bne .Next
- bset d2,InquireBits(A_DEVICE)
- .Next: addq.l #1,d2
- cmp.w #MD_NUMUNITS,d2
- blo .Loop
- movem.l (sp)+,d0-d2/a0-a1/a6
- rts
-
- comment |
- ;Find out what drives are in the system
- ;Exit with "drive map" in D0 (bit 0 = drive 0, bit 1 = drive 1, etc.)
-
- ;See the Hardware Reference Manual, Appendix E for a description of
- ;the procedure.
-
- ;Note: Drive zero is always present. In fact, the identification
- ;scheme does not work with drive zero (except for the half-speed drive).
-
- Inquire:
- movem.l d1-d5/a0,-(sp)
- moveq #4,d3
- moveq #1,d2 ;drive count
- moveq #1,d0 ;drive map (drive zero always present)
- moveq #7,d1
- move.l #ciabprb,a0
-
- bsr Deselect
-
- .InquireLoop:
- moveq #31,d4
- moveq #0,d5 ;identification longword
-
- bclr d1,(a0) ;motor on
- bset d3,(a0) ;deselect drive
- bclr d3,(a0) ;select drive
-
- bset d1,(a0) ;motor off
- bset d3,(a0) ;deselect drive
- bclr d3,(a0) ;select drive
- bset d3,(a0) ;deselect drive
-
- .ReadIdent:
- bclr d3,(a0) ;select drive
- btst #5,ciaapra ;test ready
- beq .zero
- bset d4,d5
- .zero: bset d3,(a0) ;deselect drive
- dbra d4,.ReadIdent
-
- tst.l d5
- bne .Skip
- bset d2,d0
- .Skip: addq.l #1,d3
- addq.l #1,d2
- cmp.b #4,d2
- bne .InquireLoop
- movem.l (sp)+,d1-d5/a0
- rts
- |
-
- SeekZeroAll:
- movem.l d0-d2/A_UNIT,-(sp)
- move.b InquireBits(A_DEVICE),d0
- lea Unit0(A_DEVICE),A_UNIT
- moveq #0,d1
- moveq #MD_NUMUNITS-1,d2
- .Loop: btst d1,d0
- beq .Next
- bsr SeekZero
- .Next: lea MyUnit_Sizeof(A_UNIT),A_UNIT
- addq.b #1,d1
- dbra d2,.Loop
- movem.l (sp)+,d0-d2/A_UNIT
- rts
-
- SeekZero:
- ;Places the drive in UnitNum(A_UNIT) on track zero.
- ;Drive does not need to be selected in advance.
- ;Drive will be left _deselected_!
-
- movem.l d0-d1,-(sp)
-
- .StepLoop:
- bsr SelectDriveSameMotor
- bset #CIAB_DSKDIREC,ciabprb ;set to "out" (lower tracks)
- btst #CIAB_DSKTRACK0,ciaapra ;check track zero flag
- beq .EndStepLoop
- bset #CIAB_DSKSTEP,ciabprb
- bclr #CIAB_DSKSTEP,ciabprb ;step head
- bset #CIAB_DSKSTEP,ciabprb
- bsr Deselect
- move.l TDU_CALIBRATEDELAY(A_UNIT),d0
- bsr delay
- bra .StepLoop
-
- .EndStepLoop:
- bsr Deselect
- move.l TDU_SETTLEDELAY(A_UNIT),d0
- bsr delay
- clr.w TDU_CURRTRK(A_UNIT)
- movem.l (sp)+,d0-d1
- rts
-
- CopyMemSlow:
- ;Only to be called by CopyMem
- move.l 4,a6
- SYS CopyMem
- movem.l (sp)+,d0-d7/a0-a6
- rts
-
- CopyMem:
-
- ;A0 - source
- ;A1 - destination
- ;D0 - size
-
- movem.l d0-d7/a0-a6,-(sp)
-
- move.l a0,d1
- btst #0,d1
- bne CopyMemSlow
- move.l a1,d1
- btst #0,d1
- bne CopyMemSlow
-
-
- .More: cmp.l #512,d0
- blo CopyMemSlow
-
- ;Copy 480 bytes
- n set 0
- REPT 10
- movem.l (a0)+,d1-d7/a2-a6
- movem.l d1-d7/a2-a6,n*48(a1)
- n set n+1
- ENDR
- ;Copy 32 bytes
- movem.l (a0)+,d1-d7/a2
- movem.l d1-d7/a2,480(a1)
- lea 512(a1),a1
- sub.l #512,d0
- bne .More
- movem.l (sp)+,d0-d7/a0-a6
- rts
-
- ;Debugging stuff
-
- ifne INFO_LEVEL ;If any debugging enabled at all
- KPutFmt: move.l a2,-(sp)
- lea KPutChar(pc),a2
- bsr KDoFmt
- move.l (sp)+,a2
- rts
-
- KDoFmt: move.l a6,-(sp)
- move.l 4,a6
- SYS RawDoFmt
- move.l (sp)+,a6
- rts
-
- KPutChar:
-
- ;Serial
- comment |
- move.l a6,-(sp)
- move.l 4,a6
- SYS RawPutChar
- move.l (sp)+,a6
- rts
- |
-
- ;Printer
- comment |
- move.b #$ff,$bfe301
- .Print: btst #0,$bfd000
- bne .Print
- move.b d0,$bfe101
- rts
- |
-
- ;Memory
- tst.l MemPtr
- bne .OK
- move.l #$500000,MemPtr
- .OK:
- push a0
- move.l MemPtr(pc),a0
- move.b d0,(a0)+
- move.l a0,MemPtr
- pop a0
- rts
-
- MemPtr: dc.l 0
-
- endc
-
- EndCode:
-